@memberjunction/ai-prompts 2.103.0 → 2.105.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -1
- package/dist/AIPromptRunner.d.ts +9 -1
- package/dist/AIPromptRunner.d.ts.map +1 -1
- package/dist/AIPromptRunner.js +284 -104
- package/dist/AIPromptRunner.js.map +1 -1
- package/package.json +7 -7
package/dist/AIPromptRunner.js
CHANGED
|
@@ -25,13 +25,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.LoadAIPromptRunner = exports.AIPromptRunner = void 0;
|
|
27
27
|
const ai_1 = require("@memberjunction/ai");
|
|
28
|
+
const ai_core_plus_1 = require("@memberjunction/ai-core-plus");
|
|
28
29
|
const core_1 = require("@memberjunction/core");
|
|
29
30
|
const global_1 = require("@memberjunction/global");
|
|
30
31
|
const templates_1 = require("@memberjunction/templates");
|
|
31
32
|
const ExecutionPlanner_1 = require("./ExecutionPlanner");
|
|
32
33
|
const ParallelExecutionCoordinator_1 = require("./ParallelExecutionCoordinator");
|
|
33
34
|
const aiengine_1 = require("@memberjunction/aiengine");
|
|
34
|
-
const
|
|
35
|
+
const ai_core_plus_2 = require("@memberjunction/ai-core-plus");
|
|
35
36
|
const JSON5 = __importStar(require("json5"));
|
|
36
37
|
class AIPromptRunner {
|
|
37
38
|
constructor() {
|
|
@@ -61,8 +62,11 @@ class AIPromptRunner {
|
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
logError(error, options) {
|
|
64
|
-
|
|
65
|
+
let errorMessage = error instanceof Error ? error.message : error;
|
|
65
66
|
const errorObj = error instanceof Error ? error : undefined;
|
|
67
|
+
if (options?.maxErrorLength !== undefined && errorMessage.length > options.maxErrorLength) {
|
|
68
|
+
errorMessage = errorMessage.substring(0, options.maxErrorLength) + '... [truncated]';
|
|
69
|
+
}
|
|
66
70
|
const metadata = {
|
|
67
71
|
...options?.metadata
|
|
68
72
|
};
|
|
@@ -191,7 +195,8 @@ class AIPromptRunner {
|
|
|
191
195
|
metadata: {
|
|
192
196
|
executionPhase: 'main-execution',
|
|
193
197
|
hasChildPrompts: !!params.childPrompts?.length
|
|
194
|
-
}
|
|
198
|
+
},
|
|
199
|
+
maxErrorLength: params.maxErrorLength
|
|
195
200
|
});
|
|
196
201
|
const endTime = new Date();
|
|
197
202
|
const executionTimeMS = endTime.getTime() - startTime.getTime();
|
|
@@ -215,7 +220,8 @@ class AIPromptRunner {
|
|
|
215
220
|
metadata: {
|
|
216
221
|
promptRunId: promptRun.ID,
|
|
217
222
|
errorMessage: promptRun.LatestResult?.Message
|
|
218
|
-
}
|
|
223
|
+
},
|
|
224
|
+
maxErrorLength: params.maxErrorLength
|
|
219
225
|
});
|
|
220
226
|
}
|
|
221
227
|
}
|
|
@@ -239,15 +245,19 @@ class AIPromptRunner {
|
|
|
239
245
|
let modelSelectionInfo = existingModelSelectionInfo;
|
|
240
246
|
let vendorDriverClass;
|
|
241
247
|
let vendorApiName;
|
|
248
|
+
let vendorSupportsEffortLevel;
|
|
249
|
+
let allCandidates = [];
|
|
242
250
|
if (modelSelectionInfo) {
|
|
243
|
-
const vendorID = modelSelectionInfo.vendorSelected
|
|
251
|
+
const vendorID = modelSelectionInfo.vendorSelected?.ID;
|
|
244
252
|
const modelID = modelSelectionInfo.modelSelected.ID;
|
|
245
253
|
const modelVendor = aiengine_1.AIEngine.Instance.ModelVendors.find(mv => mv.VendorID === vendorID &&
|
|
246
254
|
mv.ModelID === modelID);
|
|
247
255
|
if (modelVendor) {
|
|
248
256
|
vendorDriverClass = modelVendor.DriverClass;
|
|
249
257
|
vendorApiName = modelVendor.APIName;
|
|
258
|
+
vendorSupportsEffortLevel = modelVendor.SupportsEffortLevel;
|
|
250
259
|
}
|
|
260
|
+
allCandidates = this.buildCandidatesFromSelectionInfo(modelSelectionInfo);
|
|
251
261
|
}
|
|
252
262
|
if (!selectedModel) {
|
|
253
263
|
let modelSelectionPrompt = prompt;
|
|
@@ -259,7 +269,9 @@ class AIPromptRunner {
|
|
|
259
269
|
selectedModel = modelResult.model;
|
|
260
270
|
vendorDriverClass = modelResult.vendorDriverClass;
|
|
261
271
|
vendorApiName = modelResult.vendorApiName;
|
|
272
|
+
vendorSupportsEffortLevel = modelResult.vendorSupportsEffortLevel;
|
|
262
273
|
modelSelectionInfo = modelResult.selectionInfo;
|
|
274
|
+
allCandidates = modelResult.allCandidates || [];
|
|
263
275
|
if (!selectedModel) {
|
|
264
276
|
throw new Error(`No suitable model found for prompt ${modelSelectionPrompt.Name}`);
|
|
265
277
|
}
|
|
@@ -271,7 +283,7 @@ class AIPromptRunner {
|
|
|
271
283
|
if (params.cancellationToken?.aborted) {
|
|
272
284
|
throw new Error('Prompt execution was cancelled before model execution');
|
|
273
285
|
}
|
|
274
|
-
const { modelResult, parsedResult, validationAttempts, cumulativeTokens } = await this.executeWithValidationRetries(selectedModel, renderedPromptText, prompt, params, promptRun, vendorDriverClass, vendorApiName);
|
|
286
|
+
const { modelResult, parsedResult, validationAttempts, cumulativeTokens } = await this.executeWithValidationRetries(selectedModel, renderedPromptText, prompt, params, promptRun, allCandidates, vendorDriverClass, vendorApiName, vendorSupportsEffortLevel);
|
|
275
287
|
const endTime = new Date();
|
|
276
288
|
const executionTimeMS = endTime.getTime() - startTime.getTime();
|
|
277
289
|
await this.updatePromptRun(promptRun, prompt, modelResult, parsedResult, endTime, executionTimeMS, validationAttempts, cumulativeTokens);
|
|
@@ -414,7 +426,8 @@ class AIPromptRunner {
|
|
|
414
426
|
promptRunId: consolidatedPromptRun.ID,
|
|
415
427
|
executionTasks: executionTasks.length,
|
|
416
428
|
successfulResults: successfulResults.length
|
|
417
|
-
}
|
|
429
|
+
},
|
|
430
|
+
maxErrorLength: params.maxErrorLength
|
|
418
431
|
});
|
|
419
432
|
}
|
|
420
433
|
const additionalResults = [];
|
|
@@ -559,7 +572,8 @@ class AIPromptRunner {
|
|
|
559
572
|
category: 'ChildTemplateRendering',
|
|
560
573
|
metadata: {
|
|
561
574
|
placeholder: childParam.parentPlaceholder
|
|
562
|
-
}
|
|
575
|
+
},
|
|
576
|
+
maxErrorLength: params.maxErrorLength
|
|
563
577
|
});
|
|
564
578
|
return {
|
|
565
579
|
placeholder: childParam.parentPlaceholder,
|
|
@@ -578,7 +592,8 @@ class AIPromptRunner {
|
|
|
578
592
|
failedCount: failedChildren.length,
|
|
579
593
|
totalCount: childResults.length,
|
|
580
594
|
failedPlaceholders: failedChildren.map(fc => fc.placeholder)
|
|
581
|
-
}
|
|
595
|
+
},
|
|
596
|
+
maxErrorLength: params.maxErrorLength
|
|
582
597
|
});
|
|
583
598
|
throw new Error(`Failed to render ${failedChildren.length} child prompt templates: ${failedChildren.map(fc => fc.placeholder).join(', ')}`);
|
|
584
599
|
}
|
|
@@ -600,7 +615,7 @@ class AIPromptRunner {
|
|
|
600
615
|
if (!template) {
|
|
601
616
|
throw new Error(`Template with ID ${prompt.TemplateID} not found for prompt ${prompt.Name}`);
|
|
602
617
|
}
|
|
603
|
-
const systemPlaceholders = await
|
|
618
|
+
const systemPlaceholders = await ai_core_plus_2.SystemPlaceholderManager.resolveAllPlaceholders(params);
|
|
604
619
|
const mergedData = {
|
|
605
620
|
...systemPlaceholders,
|
|
606
621
|
...params.data,
|
|
@@ -624,7 +639,8 @@ class AIPromptRunner {
|
|
|
624
639
|
metadata: {
|
|
625
640
|
childPromptCount: params.childPrompts?.length || 0,
|
|
626
641
|
templateId: prompt.TemplateID
|
|
627
|
-
}
|
|
642
|
+
},
|
|
643
|
+
maxErrorLength: params.maxErrorLength
|
|
628
644
|
});
|
|
629
645
|
throw error;
|
|
630
646
|
}
|
|
@@ -654,18 +670,23 @@ class AIPromptRunner {
|
|
|
654
670
|
this.logError(`No suitable model candidates found for prompt ${prompt.Name}`, {
|
|
655
671
|
category: 'ModelSelection',
|
|
656
672
|
prompt: prompt,
|
|
657
|
-
severity: 'critical'
|
|
673
|
+
severity: 'critical',
|
|
674
|
+
maxErrorLength: params?.maxErrorLength
|
|
658
675
|
});
|
|
659
676
|
return {
|
|
660
677
|
model: null,
|
|
661
|
-
|
|
678
|
+
vendorDriverClass: undefined,
|
|
679
|
+
vendorApiName: undefined,
|
|
680
|
+
vendorSupportsEffortLevel: undefined,
|
|
681
|
+
allCandidates: [],
|
|
682
|
+
selectionInfo: this.createSelectionInfo({
|
|
662
683
|
aiConfiguration: configuration,
|
|
663
684
|
modelsConsidered: [],
|
|
664
685
|
modelSelected: undefined,
|
|
665
686
|
selectionReason: 'No suitable model candidates found',
|
|
666
687
|
fallbackUsed: false,
|
|
667
688
|
selectionStrategy
|
|
668
|
-
}
|
|
689
|
+
})
|
|
669
690
|
};
|
|
670
691
|
}
|
|
671
692
|
const { selected, consideredModels } = await this.selectModelWithAPIKeyTracked(candidates, params);
|
|
@@ -675,14 +696,16 @@ class AIPromptRunner {
|
|
|
675
696
|
model: null,
|
|
676
697
|
vendorDriverClass: undefined,
|
|
677
698
|
vendorApiName: undefined,
|
|
678
|
-
|
|
699
|
+
vendorSupportsEffortLevel: undefined,
|
|
700
|
+
allCandidates: candidates,
|
|
701
|
+
selectionInfo: this.createSelectionInfo({
|
|
679
702
|
aiConfiguration: configuration,
|
|
680
703
|
modelsConsidered,
|
|
681
704
|
modelSelected: undefined,
|
|
682
705
|
selectionReason: 'No API keys found for any model-vendor combination',
|
|
683
706
|
fallbackUsed: false,
|
|
684
707
|
selectionStrategy
|
|
685
|
-
}
|
|
708
|
+
})
|
|
686
709
|
};
|
|
687
710
|
}
|
|
688
711
|
let selectionReason = `Selected ${selected.model.Name} via ${selected.vendorName || 'default vendor'}`;
|
|
@@ -710,7 +733,9 @@ class AIPromptRunner {
|
|
|
710
733
|
model: selected.model,
|
|
711
734
|
vendorDriverClass: selected.driverClass,
|
|
712
735
|
vendorApiName: selected.apiName,
|
|
713
|
-
|
|
736
|
+
vendorSupportsEffortLevel: selected.supportsEffortLevel,
|
|
737
|
+
allCandidates: candidates,
|
|
738
|
+
selectionInfo: this.createSelectionInfo({
|
|
714
739
|
aiConfiguration: configuration,
|
|
715
740
|
modelsConsidered,
|
|
716
741
|
modelSelected: selected.model,
|
|
@@ -718,26 +743,29 @@ class AIPromptRunner {
|
|
|
718
743
|
selectionReason,
|
|
719
744
|
fallbackUsed,
|
|
720
745
|
selectionStrategy
|
|
721
|
-
}
|
|
746
|
+
})
|
|
722
747
|
};
|
|
723
748
|
}
|
|
724
749
|
catch (error) {
|
|
725
750
|
this.logError(error, {
|
|
726
751
|
category: 'ModelSelection',
|
|
727
|
-
prompt: prompt
|
|
752
|
+
prompt: prompt,
|
|
753
|
+
maxErrorLength: params?.maxErrorLength
|
|
728
754
|
});
|
|
729
755
|
return {
|
|
730
756
|
model: null,
|
|
731
757
|
vendorDriverClass: undefined,
|
|
732
758
|
vendorApiName: undefined,
|
|
733
|
-
|
|
759
|
+
vendorSupportsEffortLevel: undefined,
|
|
760
|
+
allCandidates: [],
|
|
761
|
+
selectionInfo: this.createSelectionInfo({
|
|
734
762
|
aiConfiguration: configuration,
|
|
735
763
|
modelsConsidered: [],
|
|
736
764
|
modelSelected: undefined,
|
|
737
765
|
selectionReason: `Error during model selection: ${error.message}`,
|
|
738
766
|
fallbackUsed: false,
|
|
739
767
|
selectionStrategy: 'Default'
|
|
740
|
-
}
|
|
768
|
+
})
|
|
741
769
|
};
|
|
742
770
|
}
|
|
743
771
|
}
|
|
@@ -758,6 +786,7 @@ class AIPromptRunner {
|
|
|
758
786
|
vendorName: preferredVendor.Vendor,
|
|
759
787
|
driverClass: preferredVendor.DriverClass || model.DriverClass,
|
|
760
788
|
apiName: preferredVendor.APIName || model.APIName,
|
|
789
|
+
supportsEffortLevel: preferredVendor.SupportsEffortLevel ?? model.SupportsEffortLevel ?? false,
|
|
761
790
|
isPreferredVendor: true,
|
|
762
791
|
priority: basePriority + 1000,
|
|
763
792
|
source
|
|
@@ -772,6 +801,7 @@ class AIPromptRunner {
|
|
|
772
801
|
vendorName: vendor.Vendor,
|
|
773
802
|
driverClass: vendor.DriverClass || model.DriverClass,
|
|
774
803
|
apiName: vendor.APIName || model.APIName,
|
|
804
|
+
supportsEffortLevel: vendor.SupportsEffortLevel ?? model.SupportsEffortLevel ?? false,
|
|
775
805
|
isPreferredVendor: false,
|
|
776
806
|
priority: basePriority + (vendor.Priority || 0),
|
|
777
807
|
source
|
|
@@ -783,6 +813,7 @@ class AIPromptRunner {
|
|
|
783
813
|
model,
|
|
784
814
|
driverClass: model.DriverClass,
|
|
785
815
|
apiName: model.APIName,
|
|
816
|
+
supportsEffortLevel: model.SupportsEffortLevel ?? false,
|
|
786
817
|
isPreferredVendor: false,
|
|
787
818
|
priority: basePriority,
|
|
788
819
|
source
|
|
@@ -808,6 +839,7 @@ class AIPromptRunner {
|
|
|
808
839
|
vendorName: preferredVendor.Vendor,
|
|
809
840
|
driverClass: preferredVendor.DriverClass || model.DriverClass,
|
|
810
841
|
apiName: preferredVendor.APIName || model.APIName,
|
|
842
|
+
supportsEffortLevel: preferredVendor.SupportsEffortLevel ?? model.SupportsEffortLevel ?? false,
|
|
811
843
|
isPreferredVendor: true,
|
|
812
844
|
priority: basePriority + 1000,
|
|
813
845
|
source: 'prompt-model'
|
|
@@ -822,6 +854,7 @@ class AIPromptRunner {
|
|
|
822
854
|
vendorName: vendor.Vendor,
|
|
823
855
|
driverClass: vendor.DriverClass || model.DriverClass,
|
|
824
856
|
apiName: vendor.APIName || model.APIName,
|
|
857
|
+
supportsEffortLevel: vendor.SupportsEffortLevel ?? model.SupportsEffortLevel ?? false,
|
|
825
858
|
isPreferredVendor: false,
|
|
826
859
|
priority: basePriority + (vendor.Priority || 0) * 10,
|
|
827
860
|
source: 'prompt-model'
|
|
@@ -833,6 +866,7 @@ class AIPromptRunner {
|
|
|
833
866
|
model,
|
|
834
867
|
driverClass: model.DriverClass,
|
|
835
868
|
apiName: model.APIName,
|
|
869
|
+
supportsEffortLevel: model.SupportsEffortLevel ?? false,
|
|
836
870
|
isPreferredVendor: false,
|
|
837
871
|
priority: basePriority,
|
|
838
872
|
source: 'prompt-model'
|
|
@@ -898,6 +932,21 @@ class AIPromptRunner {
|
|
|
898
932
|
candidates.push(...modelCandidates);
|
|
899
933
|
}
|
|
900
934
|
}
|
|
935
|
+
if (configurationId) {
|
|
936
|
+
const nullConfigModels = aiengine_1.AIEngine.Instance.PromptModels.filter(pm => pm.PromptID === prompt.ID &&
|
|
937
|
+
(pm.Status === 'Active' || pm.Status === 'Preview') &&
|
|
938
|
+
!pm.ConfigurationID);
|
|
939
|
+
if (nullConfigModels.length > 0 && verbose) {
|
|
940
|
+
(0, core_1.LogStatus)(`Adding ${nullConfigModels.length} NULL configuration models as fallback candidates`);
|
|
941
|
+
}
|
|
942
|
+
for (const pm of nullConfigModels) {
|
|
943
|
+
const model = aiengine_1.AIEngine.Instance.Models.find(m => m.ID === pm.ModelID);
|
|
944
|
+
if (model && model.IsActive) {
|
|
945
|
+
const modelCandidates = createCandidatesForModel(model, 2000, 'prompt-model', pm.Priority);
|
|
946
|
+
candidates.push(...modelCandidates);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
901
950
|
}
|
|
902
951
|
else {
|
|
903
952
|
let modelPool = aiengine_1.AIEngine.Instance.Models.filter(m => m.IsActive &&
|
|
@@ -938,12 +987,35 @@ class AIPromptRunner {
|
|
|
938
987
|
candidates.sort((a, b) => b.priority - a.priority);
|
|
939
988
|
return candidates;
|
|
940
989
|
}
|
|
990
|
+
createSelectionInfo(data) {
|
|
991
|
+
const info = new ai_core_plus_1.AIModelSelectionInfo();
|
|
992
|
+
Object.assign(info, data);
|
|
993
|
+
return info;
|
|
994
|
+
}
|
|
995
|
+
buildCandidatesFromSelectionInfo(selectionInfo) {
|
|
996
|
+
const validModels = selectionInfo.extractValidCandidates();
|
|
997
|
+
return validModels.map(considered => {
|
|
998
|
+
const modelVendor = considered.vendor
|
|
999
|
+
? aiengine_1.AIEngine.Instance.ModelVendors.find(mv => mv.ModelID === considered.model.ID &&
|
|
1000
|
+
mv.VendorID === considered.vendor.ID)
|
|
1001
|
+
: undefined;
|
|
1002
|
+
return {
|
|
1003
|
+
model: considered.model,
|
|
1004
|
+
vendorId: considered.vendor?.ID,
|
|
1005
|
+
vendorName: considered.vendor?.Name,
|
|
1006
|
+
driverClass: modelVendor?.DriverClass || considered.model.DriverClass,
|
|
1007
|
+
apiName: modelVendor?.APIName || considered.model.APIName,
|
|
1008
|
+
supportsEffortLevel: modelVendor?.SupportsEffortLevel ?? considered.model.SupportsEffortLevel ?? false,
|
|
1009
|
+
isPreferredVendor: false,
|
|
1010
|
+
priority: considered.priority,
|
|
1011
|
+
source: (selectionInfo.selectionStrategy === 'ByPower' ? 'power-rank' : 'model-type')
|
|
1012
|
+
};
|
|
1013
|
+
}).sort((a, b) => b.priority - a.priority);
|
|
1014
|
+
}
|
|
941
1015
|
async selectModelWithAPIKeyTracked(candidates, params) {
|
|
942
1016
|
const checkedDrivers = new Map();
|
|
943
1017
|
const consideredModels = [];
|
|
944
|
-
let attemptCount = 0;
|
|
945
1018
|
for (const candidate of candidates) {
|
|
946
|
-
attemptCount++;
|
|
947
1019
|
let hasKey;
|
|
948
1020
|
if (checkedDrivers.has(candidate.driverClass)) {
|
|
949
1021
|
hasKey = checkedDrivers.get(candidate.driverClass);
|
|
@@ -964,25 +1036,31 @@ class AIPromptRunner {
|
|
|
964
1036
|
available: hasKey,
|
|
965
1037
|
unavailableReason: hasKey ? undefined : `No API key for driver ${candidate.driverClass}`
|
|
966
1038
|
});
|
|
967
|
-
if (hasKey) {
|
|
968
|
-
this.logStatus(` Selected model ${candidate.model.Name} with ${candidate.vendorName || 'default'} vendor (driver: ${candidate.driverClass})`, true);
|
|
969
|
-
if (candidate.isPreferredVendor) {
|
|
970
|
-
this.logStatus(` Using preferred vendor${candidate.vendorId ? ` (${candidate.vendorName})` : ''}`, true, params);
|
|
971
|
-
}
|
|
972
|
-
this.logStatus(` Checked ${attemptCount} candidate(s) before finding valid API key`, true, params);
|
|
973
|
-
return { selected: candidate, consideredModels };
|
|
974
|
-
}
|
|
975
1039
|
}
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1040
|
+
const selected = consideredModels.find(m => m.available);
|
|
1041
|
+
const selectedCandidate = selected ? candidates.find(c => c.model.ID === selected.model.ID &&
|
|
1042
|
+
c.vendorId === selected.vendor?.ID) : null;
|
|
1043
|
+
if (selectedCandidate) {
|
|
1044
|
+
const validCount = consideredModels.filter(m => m.available).length;
|
|
1045
|
+
this.logStatus(` Selected model ${selectedCandidate.model.Name} with ${selectedCandidate.vendorName || 'default'} vendor (driver: ${selectedCandidate.driverClass})`, true);
|
|
1046
|
+
if (selectedCandidate.isPreferredVendor) {
|
|
1047
|
+
this.logStatus(` Using preferred vendor${selectedCandidate.vendorId ? ` (${selectedCandidate.vendorName})` : ''}`, true, params);
|
|
983
1048
|
}
|
|
984
|
-
|
|
985
|
-
|
|
1049
|
+
this.logStatus(` Found ${validCount} valid candidate(s) out of ${candidates.length} total`, true, params);
|
|
1050
|
+
}
|
|
1051
|
+
else {
|
|
1052
|
+
const triedSummary = candidates.slice(0, 5).map(c => `${c.model.Name}/${c.vendorName || 'default'}(${c.driverClass})`).join(', ');
|
|
1053
|
+
this.logError(`No API keys found for any model-vendor combination. Tried: ${triedSummary}${candidates.length > 5 ? `... (${candidates.length} total)` : ''}`, {
|
|
1054
|
+
category: 'APIKeyValidation',
|
|
1055
|
+
severity: 'critical',
|
|
1056
|
+
metadata: {
|
|
1057
|
+
candidatesChecked: candidates.length,
|
|
1058
|
+
modelsChecked: consideredModels.length
|
|
1059
|
+
},
|
|
1060
|
+
maxErrorLength: params?.maxErrorLength
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
return { selected: selectedCandidate, consideredModels };
|
|
986
1064
|
}
|
|
987
1065
|
async createPromptRun(prompt, model, params, systemPromptText, startTime, vendorId, modelSelectionInfo) {
|
|
988
1066
|
const promptRun = await this._metadata.GetEntityObject('MJ: AI Prompt Runs', params.contextUser);
|
|
@@ -1020,13 +1098,12 @@ class AIPromptRunner {
|
|
|
1020
1098
|
promptRun.ModelPowerRank = model.PowerRank;
|
|
1021
1099
|
}
|
|
1022
1100
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
promptRunWithFailover.TotalFailoverDuration = 0;
|
|
1101
|
+
promptRun.OriginalModelID = model.ID;
|
|
1102
|
+
promptRun.OriginalRequestStartTime = startTime;
|
|
1103
|
+
promptRun.FailoverAttempts = 0;
|
|
1104
|
+
promptRun.FailoverErrors = null;
|
|
1105
|
+
promptRun.FailoverDurations = null;
|
|
1106
|
+
promptRun.TotalFailoverDuration = 0;
|
|
1030
1107
|
const modelWithVendor = model;
|
|
1031
1108
|
if (modelSelectionInfo) {
|
|
1032
1109
|
promptRun.VendorID = modelSelectionInfo.vendorSelected?.ID || vendorId || modelWithVendor._selectedVendorId;
|
|
@@ -1148,7 +1225,8 @@ class AIPromptRunner {
|
|
|
1148
1225
|
promptId: prompt.ID,
|
|
1149
1226
|
modelId: model.ID,
|
|
1150
1227
|
vendorId
|
|
1151
|
-
}
|
|
1228
|
+
},
|
|
1229
|
+
maxErrorLength: params.maxErrorLength
|
|
1152
1230
|
});
|
|
1153
1231
|
throw new Error(error);
|
|
1154
1232
|
}
|
|
@@ -1169,7 +1247,8 @@ class AIPromptRunner {
|
|
|
1169
1247
|
metadata: {
|
|
1170
1248
|
promptRunId: promptRun.ID,
|
|
1171
1249
|
saveError: promptRun.LatestResult?.Message
|
|
1172
|
-
}
|
|
1250
|
+
},
|
|
1251
|
+
maxErrorLength: params.maxErrorLength
|
|
1173
1252
|
});
|
|
1174
1253
|
throw new Error(msg);
|
|
1175
1254
|
}
|
|
@@ -1180,7 +1259,7 @@ class AIPromptRunner {
|
|
|
1180
1259
|
if (!templateContent) {
|
|
1181
1260
|
throw new Error(`No content found for template ${template.Name}`);
|
|
1182
1261
|
}
|
|
1183
|
-
const systemPlaceholders = await
|
|
1262
|
+
const systemPlaceholders = await ai_core_plus_2.SystemPlaceholderManager.resolveAllPlaceholders(params);
|
|
1184
1263
|
const mergedData = {
|
|
1185
1264
|
...systemPlaceholders,
|
|
1186
1265
|
...params.data,
|
|
@@ -1195,22 +1274,23 @@ class AIPromptRunner {
|
|
|
1195
1274
|
templateId: template.ID,
|
|
1196
1275
|
templateName: template.Name,
|
|
1197
1276
|
hasChildPrompts: !!params.childPrompts?.length
|
|
1198
|
-
}
|
|
1277
|
+
},
|
|
1278
|
+
maxErrorLength: params.maxErrorLength
|
|
1199
1279
|
});
|
|
1200
1280
|
throw error;
|
|
1201
1281
|
}
|
|
1202
1282
|
}
|
|
1203
|
-
async executeModelWithFailover(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole = 'system', cancellationToken, allCandidates, promptRun, vendorDriverClass, vendorApiName) {
|
|
1283
|
+
async executeModelWithFailover(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole = 'system', cancellationToken, allCandidates, promptRun, vendorDriverClass, vendorApiName, vendorSupportsEffortLevel) {
|
|
1204
1284
|
const failoverConfig = this.getFailoverConfiguration(prompt);
|
|
1205
1285
|
if (failoverConfig.strategy === 'None') {
|
|
1206
|
-
return this.executeModel(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole, cancellationToken, vendorDriverClass, vendorApiName);
|
|
1286
|
+
return this.executeModel(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole, cancellationToken, vendorDriverClass, vendorApiName, vendorSupportsEffortLevel);
|
|
1207
1287
|
}
|
|
1208
1288
|
const failoverAttempts = [];
|
|
1209
1289
|
let lastError = null;
|
|
1210
1290
|
let currentModel = model;
|
|
1211
1291
|
let currentVendorId = vendorId;
|
|
1212
1292
|
let attemptNumber = 0;
|
|
1213
|
-
if (!allCandidates) {
|
|
1293
|
+
if (!allCandidates || allCandidates.length === 0) {
|
|
1214
1294
|
allCandidates = await this.buildFailoverCandidates(prompt);
|
|
1215
1295
|
}
|
|
1216
1296
|
while (attemptNumber <= failoverConfig.maxAttempts) {
|
|
@@ -1218,6 +1298,9 @@ class AIPromptRunner {
|
|
|
1218
1298
|
const attemptStartTime = Date.now();
|
|
1219
1299
|
try {
|
|
1220
1300
|
if (attemptNumber > 1) {
|
|
1301
|
+
const vendorName = currentVendorId
|
|
1302
|
+
? aiengine_1.AIEngine.Instance.Vendors.find(v => v.ID === currentVendorId)?.Name || 'Unknown'
|
|
1303
|
+
: 'default';
|
|
1221
1304
|
(0, core_1.LogStatusEx)({
|
|
1222
1305
|
message: `🔄 Failover attempt ${attemptNumber} with model ${currentModel.Name} (vendor: ${currentVendorId || 'default'})`,
|
|
1223
1306
|
category: 'AI',
|
|
@@ -1229,7 +1312,7 @@ class AIPromptRunner {
|
|
|
1229
1312
|
}]
|
|
1230
1313
|
});
|
|
1231
1314
|
}
|
|
1232
|
-
const result = await this.executeModel(currentModel, renderedPrompt, prompt, params, currentVendorId, conversationMessages, templateMessageRole, cancellationToken, vendorDriverClass, vendorApiName);
|
|
1315
|
+
const result = await this.executeModel(currentModel, renderedPrompt, prompt, params, currentVendorId, conversationMessages, templateMessageRole, cancellationToken, vendorDriverClass, vendorApiName, vendorSupportsEffortLevel);
|
|
1233
1316
|
if (failoverAttempts.length > 0 && promptRun) {
|
|
1234
1317
|
this.updatePromptRunWithFailoverSuccess(promptRun, failoverAttempts, currentModel, currentVendorId);
|
|
1235
1318
|
}
|
|
@@ -1249,26 +1332,25 @@ class AIPromptRunner {
|
|
|
1249
1332
|
timestamp: new Date()
|
|
1250
1333
|
};
|
|
1251
1334
|
failoverAttempts.push(failoverAttempt);
|
|
1335
|
+
allCandidates = this.filterAuthenticationFailedVendor(errorAnalysis.errorType, currentVendorId, allCandidates);
|
|
1336
|
+
const shouldContinueRateLimit = await this.handleRateLimitRetry(errorAnalysis, currentModel, currentVendorId, failoverAttempts, prompt, attemptNumber, failoverConfig.maxAttempts, failoverAttempt);
|
|
1337
|
+
if (shouldContinueRateLimit) {
|
|
1338
|
+
continue;
|
|
1339
|
+
}
|
|
1252
1340
|
const shouldFailover = this.shouldAttemptFailover(lastError, failoverConfig, attemptNumber);
|
|
1253
1341
|
if (!shouldFailover) {
|
|
1254
1342
|
this.logFailoverAttempt(prompt.ID, failoverAttempt, false);
|
|
1255
1343
|
break;
|
|
1256
1344
|
}
|
|
1257
|
-
const
|
|
1258
|
-
if (
|
|
1259
|
-
this.logFailoverAttempt(prompt.ID, failoverAttempt, false);
|
|
1345
|
+
const transitionResult = await this.transitionToNextCandidate(currentModel, currentVendorId, failoverConfig, allCandidates, failoverAttempts, prompt.ID, failoverAttempt, attemptNumber);
|
|
1346
|
+
if (!transitionResult) {
|
|
1260
1347
|
break;
|
|
1261
1348
|
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
this.logFailoverAttempt(prompt.ID, failoverAttempt, true);
|
|
1268
|
-
if (attemptNumber < failoverConfig.maxAttempts) {
|
|
1269
|
-
const delay = this.calculateFailoverDelay(attemptNumber, failoverConfig.delaySeconds);
|
|
1270
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
1271
|
-
}
|
|
1349
|
+
currentModel = transitionResult.model;
|
|
1350
|
+
currentVendorId = transitionResult.vendorId;
|
|
1351
|
+
vendorDriverClass = transitionResult.driverClass;
|
|
1352
|
+
vendorApiName = transitionResult.apiName;
|
|
1353
|
+
vendorSupportsEffortLevel = transitionResult.supportsEffortLevel;
|
|
1272
1354
|
}
|
|
1273
1355
|
}
|
|
1274
1356
|
if (promptRun && failoverAttempts.length > 0) {
|
|
@@ -1302,6 +1384,7 @@ class AIPromptRunner {
|
|
|
1302
1384
|
vendorName: undefined,
|
|
1303
1385
|
driverClass: model.DriverClass,
|
|
1304
1386
|
apiName: model.APIName,
|
|
1387
|
+
supportsEffortLevel: model.SupportsEffortLevel ?? false,
|
|
1305
1388
|
isPreferredVendor: false,
|
|
1306
1389
|
priority: model.PowerRank || 0,
|
|
1307
1390
|
source: 'power-rank'
|
|
@@ -1315,6 +1398,7 @@ class AIPromptRunner {
|
|
|
1315
1398
|
vendorName: vendor.Vendor,
|
|
1316
1399
|
driverClass: vendor.DriverClass || model.DriverClass,
|
|
1317
1400
|
apiName: vendor.APIName || model.APIName,
|
|
1401
|
+
supportsEffortLevel: vendor.SupportsEffortLevel ?? model.SupportsEffortLevel ?? false,
|
|
1318
1402
|
isPreferredVendor: vendor.Priority > 0,
|
|
1319
1403
|
priority: (model.PowerRank || 0) + (vendor.Priority || 0),
|
|
1320
1404
|
source: 'power-rank'
|
|
@@ -1325,17 +1409,16 @@ class AIPromptRunner {
|
|
|
1325
1409
|
return candidates;
|
|
1326
1410
|
}
|
|
1327
1411
|
updatePromptRunWithFailoverSuccess(promptRun, failoverAttempts, currentModel, currentVendorId) {
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
promptRunWithFailover.FailoverErrors = JSON.stringify(failoverAttempts.map(a => ({
|
|
1412
|
+
promptRun.FailoverAttempts = failoverAttempts.length;
|
|
1413
|
+
promptRun.FailoverErrors = JSON.stringify(failoverAttempts.map(a => ({
|
|
1331
1414
|
model: a.modelId,
|
|
1332
1415
|
vendor: a.vendorId,
|
|
1333
1416
|
error: a.error.message,
|
|
1334
1417
|
errorType: a.errorType
|
|
1335
1418
|
})));
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
if (currentModel.ID !==
|
|
1419
|
+
promptRun.FailoverDurations = JSON.stringify(failoverAttempts.map(a => a.duration));
|
|
1420
|
+
promptRun.TotalFailoverDuration = failoverAttempts.reduce((sum, a) => sum + a.duration, 0);
|
|
1421
|
+
if (currentModel.ID !== promptRun.OriginalModelID) {
|
|
1339
1422
|
promptRun.ModelID = currentModel.ID;
|
|
1340
1423
|
}
|
|
1341
1424
|
if (currentVendorId && currentVendorId !== promptRun.VendorID) {
|
|
@@ -1343,16 +1426,15 @@ class AIPromptRunner {
|
|
|
1343
1426
|
}
|
|
1344
1427
|
}
|
|
1345
1428
|
updatePromptRunWithFailoverFailure(promptRun, failoverAttempts) {
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
promptRunWithFailover.FailoverErrors = JSON.stringify(failoverAttempts.map(a => ({
|
|
1429
|
+
promptRun.FailoverAttempts = failoverAttempts.length;
|
|
1430
|
+
promptRun.FailoverErrors = JSON.stringify(failoverAttempts.map(a => ({
|
|
1349
1431
|
model: a.modelId,
|
|
1350
1432
|
vendor: a.vendorId,
|
|
1351
1433
|
error: a.error.message,
|
|
1352
1434
|
errorType: a.errorType
|
|
1353
1435
|
})));
|
|
1354
|
-
|
|
1355
|
-
|
|
1436
|
+
promptRun.FailoverDurations = JSON.stringify(failoverAttempts.map(a => a.duration));
|
|
1437
|
+
promptRun.TotalFailoverDuration = failoverAttempts.reduce((sum, a) => sum + a.duration, 0);
|
|
1356
1438
|
}
|
|
1357
1439
|
createFailoverErrorResult(lastError, failoverAttempts) {
|
|
1358
1440
|
const startTime = new Date();
|
|
@@ -1368,25 +1450,29 @@ class AIPromptRunner {
|
|
|
1368
1450
|
data: null
|
|
1369
1451
|
};
|
|
1370
1452
|
}
|
|
1371
|
-
async executeModel(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole = 'system', cancellationToken, vendorDriverClass, vendorApiName) {
|
|
1453
|
+
async executeModel(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole = 'system', cancellationToken, vendorDriverClass, vendorApiName, vendorSupportsEffortLevel) {
|
|
1372
1454
|
let driverClass;
|
|
1373
1455
|
let apiName;
|
|
1374
1456
|
let llm;
|
|
1375
1457
|
let chatParams;
|
|
1376
1458
|
try {
|
|
1377
1459
|
const verbose = params.verbose === true || (0, core_1.IsVerboseLoggingEnabled)();
|
|
1460
|
+
let supportsEffortLevel = false;
|
|
1378
1461
|
if (vendorDriverClass && vendorApiName) {
|
|
1379
1462
|
driverClass = vendorDriverClass;
|
|
1380
1463
|
apiName = vendorApiName;
|
|
1464
|
+
supportsEffortLevel = vendorSupportsEffortLevel ?? false;
|
|
1381
1465
|
}
|
|
1382
1466
|
else {
|
|
1383
1467
|
driverClass = model.DriverClass;
|
|
1384
1468
|
apiName = model.APIName;
|
|
1469
|
+
supportsEffortLevel = model.SupportsEffortLevel ?? false;
|
|
1385
1470
|
if (vendorId) {
|
|
1386
1471
|
const modelVendor = aiengine_1.AIEngine.Instance.ModelVendors.find((mv) => mv.ModelID === model.ID && mv.VendorID === vendorId && mv.Status === 'Active' && this.isInferenceProvider(mv));
|
|
1387
1472
|
if (modelVendor) {
|
|
1388
1473
|
driverClass = modelVendor.DriverClass || driverClass;
|
|
1389
1474
|
apiName = modelVendor.APIName || apiName;
|
|
1475
|
+
supportsEffortLevel = modelVendor.SupportsEffortLevel ?? supportsEffortLevel;
|
|
1390
1476
|
}
|
|
1391
1477
|
else {
|
|
1392
1478
|
this.logStatus(`⚠️ Vendor ${vendorId} not found or is not an inference provider for model ${model.Name}, using model defaults`, true, params);
|
|
@@ -1472,11 +1558,21 @@ class AIPromptRunner {
|
|
|
1472
1558
|
chatParams.topLogProbs = params.additionalParameters.topLogProbs;
|
|
1473
1559
|
}
|
|
1474
1560
|
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1561
|
+
const hasEffortLevel = (params.effortLevel !== undefined && params.effortLevel !== null) ||
|
|
1562
|
+
(prompt.EffortLevel !== undefined && prompt.EffortLevel !== null);
|
|
1563
|
+
if (hasEffortLevel) {
|
|
1564
|
+
if (supportsEffortLevel) {
|
|
1565
|
+
if (params.effortLevel !== undefined && params.effortLevel !== null) {
|
|
1566
|
+
chatParams.effortLevel = params.effortLevel.toString();
|
|
1567
|
+
}
|
|
1568
|
+
else if (prompt.EffortLevel !== undefined && prompt.EffortLevel !== null) {
|
|
1569
|
+
chatParams.effortLevel = prompt.EffortLevel.toString();
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
else {
|
|
1573
|
+
const effortValue = params.effortLevel ?? prompt.EffortLevel;
|
|
1574
|
+
console.log(`⚠️ Effort Level ${effortValue} specified but will be ignored - model ${model.Name} does not support effort levels`);
|
|
1575
|
+
}
|
|
1480
1576
|
}
|
|
1481
1577
|
if (prompt.ResponseFormat && prompt.ResponseFormat !== 'Any') {
|
|
1482
1578
|
chatParams.responseFormat = prompt.ResponseFormat;
|
|
@@ -1510,7 +1606,8 @@ class AIPromptRunner {
|
|
|
1510
1606
|
model: model,
|
|
1511
1607
|
metadata: {
|
|
1512
1608
|
vendorId
|
|
1513
|
-
}
|
|
1609
|
+
},
|
|
1610
|
+
maxErrorLength: params.maxErrorLength
|
|
1514
1611
|
});
|
|
1515
1612
|
throw error;
|
|
1516
1613
|
}
|
|
@@ -1542,7 +1639,7 @@ class AIPromptRunner {
|
|
|
1542
1639
|
}
|
|
1543
1640
|
return messages;
|
|
1544
1641
|
}
|
|
1545
|
-
async executeWithValidationRetries(selectedModel, renderedPromptText, prompt, params, promptRun, vendorDriverClass, vendorApiName) {
|
|
1642
|
+
async executeWithValidationRetries(selectedModel, renderedPromptText, prompt, params, promptRun, allCandidates, vendorDriverClass, vendorApiName, vendorSupportsEffortLevel) {
|
|
1546
1643
|
const validationAttempts = [];
|
|
1547
1644
|
const maxRetries = Math.max(0, prompt.MaxRetries || 0);
|
|
1548
1645
|
let lastError = null;
|
|
@@ -1558,7 +1655,7 @@ class AIPromptRunner {
|
|
|
1558
1655
|
(0, core_1.LogStatus)(` 🔄 Retrying execution due to validation failure, attempt ${attempt + 1}/${maxRetries + 1}`);
|
|
1559
1656
|
await this.applyRetryDelay(prompt, attempt);
|
|
1560
1657
|
}
|
|
1561
|
-
const modelResult = await this.executeModelWithFailover(selectedModel, renderedPromptText, prompt, params, promptRun.VendorID, params.conversationMessages, params.templateMessageRole || 'system', params.cancellationToken,
|
|
1658
|
+
const modelResult = await this.executeModelWithFailover(selectedModel, renderedPromptText, prompt, params, promptRun.VendorID, params.conversationMessages, params.templateMessageRole || 'system', params.cancellationToken, allCandidates, promptRun, vendorDriverClass, vendorApiName, vendorSupportsEffortLevel);
|
|
1562
1659
|
if (modelResult.data?.usage) {
|
|
1563
1660
|
cumulativePromptTokens += modelResult.data.usage.promptTokens || 0;
|
|
1564
1661
|
cumulativeCompletionTokens += modelResult.data.usage.completionTokens || 0;
|
|
@@ -1618,7 +1715,8 @@ class AIPromptRunner {
|
|
|
1618
1715
|
attempt: attempt + 1,
|
|
1619
1716
|
maxRetries: maxRetries + 1,
|
|
1620
1717
|
modelName: selectedModel.Name
|
|
1621
|
-
}
|
|
1718
|
+
},
|
|
1719
|
+
maxErrorLength: params.maxErrorLength
|
|
1622
1720
|
});
|
|
1623
1721
|
const validationAttempt = {
|
|
1624
1722
|
attemptNumber: attempt + 1,
|
|
@@ -1635,7 +1733,10 @@ class AIPromptRunner {
|
|
|
1635
1733
|
}
|
|
1636
1734
|
throw lastError || new Error('Execution failed after all retry attempts');
|
|
1637
1735
|
}
|
|
1638
|
-
|
|
1736
|
+
calculateRetryDelay(prompt, attemptNumber, suggestedDelaySeconds) {
|
|
1737
|
+
if (suggestedDelaySeconds && suggestedDelaySeconds > 0) {
|
|
1738
|
+
return suggestedDelaySeconds * 1000;
|
|
1739
|
+
}
|
|
1639
1740
|
const baseDelay = prompt.RetryDelayMS || 1000;
|
|
1640
1741
|
let delay = baseDelay;
|
|
1641
1742
|
switch (prompt.RetryStrategy) {
|
|
@@ -1651,9 +1752,73 @@ class AIPromptRunner {
|
|
|
1651
1752
|
default:
|
|
1652
1753
|
delay = baseDelay;
|
|
1653
1754
|
}
|
|
1654
|
-
|
|
1755
|
+
return delay;
|
|
1756
|
+
}
|
|
1757
|
+
async applyRetryDelay(prompt, attemptNumber, suggestedDelaySeconds) {
|
|
1758
|
+
const delay = this.calculateRetryDelay(prompt, attemptNumber, suggestedDelaySeconds);
|
|
1759
|
+
const delaySeconds = (delay / 1000).toFixed(1);
|
|
1760
|
+
(0, core_1.LogStatus)(` Waiting ${delaySeconds}s before retry (strategy: ${prompt.RetryStrategy || 'Fixed'})...`);
|
|
1655
1761
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
1656
1762
|
}
|
|
1763
|
+
filterAuthenticationFailedVendor(errorType, currentVendorId, allCandidates) {
|
|
1764
|
+
if (errorType !== 'Authentication') {
|
|
1765
|
+
return allCandidates;
|
|
1766
|
+
}
|
|
1767
|
+
const failedVendorId = currentVendorId || 'default';
|
|
1768
|
+
const beforeCount = allCandidates.length;
|
|
1769
|
+
const filteredCandidates = allCandidates.filter(c => (c.vendorId || 'default') !== failedVendorId);
|
|
1770
|
+
const removedCount = beforeCount - filteredCandidates.length;
|
|
1771
|
+
if (removedCount > 0) {
|
|
1772
|
+
const vendorName = aiengine_1.AIEngine.Instance.Vendors.find(v => v.ID === failedVendorId)?.Name || failedVendorId;
|
|
1773
|
+
const remainingCount = filteredCandidates.length;
|
|
1774
|
+
this.logStatus(` 🔒 Invalid API key for ${vendorName} - excluding ${removedCount} model${removedCount === 1 ? '' : 's'} from this vendor (${remainingCount} remaining)`, true);
|
|
1775
|
+
}
|
|
1776
|
+
return filteredCandidates;
|
|
1777
|
+
}
|
|
1778
|
+
async handleRateLimitRetry(errorAnalysis, currentModel, currentVendorId, failoverAttempts, prompt, attemptNumber, maxAttempts, failoverAttempt) {
|
|
1779
|
+
const isRateLimit = errorAnalysis.errorType === 'RateLimit';
|
|
1780
|
+
if (!isRateLimit) {
|
|
1781
|
+
return false;
|
|
1782
|
+
}
|
|
1783
|
+
const rateLimitRetryCount = failoverAttempts.filter(a => a.modelId === currentModel.ID &&
|
|
1784
|
+
a.vendorId === currentVendorId &&
|
|
1785
|
+
a.errorType === 'RateLimit').length;
|
|
1786
|
+
const maxRetries = prompt.MaxRetries ?? 3;
|
|
1787
|
+
const shouldRetry = rateLimitRetryCount <= maxRetries;
|
|
1788
|
+
if (shouldRetry) {
|
|
1789
|
+
const modelName = currentModel.Name;
|
|
1790
|
+
const vendorName = currentVendorId
|
|
1791
|
+
? aiengine_1.AIEngine.Instance.Vendors.find(v => v.ID === currentVendorId)?.Name || 'default'
|
|
1792
|
+
: 'default';
|
|
1793
|
+
this.logStatus(` ⏳ Rate limit hit - retrying ${modelName} (${vendorName}) with backoff (attempt ${rateLimitRetryCount}/${maxRetries})`, true);
|
|
1794
|
+
this.logFailoverAttempt(prompt.ID, failoverAttempt, true);
|
|
1795
|
+
if (attemptNumber < maxAttempts) {
|
|
1796
|
+
await this.applyRetryDelay(prompt, rateLimitRetryCount, errorAnalysis.suggestedRetryDelaySeconds);
|
|
1797
|
+
}
|
|
1798
|
+
return true;
|
|
1799
|
+
}
|
|
1800
|
+
return false;
|
|
1801
|
+
}
|
|
1802
|
+
async transitionToNextCandidate(currentModel, currentVendorId, failoverConfig, allCandidates, failoverAttempts, promptId, failoverAttempt, attemptNumber) {
|
|
1803
|
+
const nextCandidates = this.selectFailoverCandidates(currentModel, currentVendorId, failoverConfig.strategy, failoverConfig.modelStrategy, allCandidates, failoverAttempts);
|
|
1804
|
+
if (nextCandidates.length === 0) {
|
|
1805
|
+
this.logFailoverAttempt(promptId, failoverAttempt, false);
|
|
1806
|
+
return null;
|
|
1807
|
+
}
|
|
1808
|
+
const nextCandidate = nextCandidates[0];
|
|
1809
|
+
this.logFailoverAttempt(promptId, failoverAttempt, true);
|
|
1810
|
+
if (attemptNumber < failoverConfig.maxAttempts) {
|
|
1811
|
+
const delay = this.calculateFailoverDelay(attemptNumber, failoverConfig.delaySeconds);
|
|
1812
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
1813
|
+
}
|
|
1814
|
+
return {
|
|
1815
|
+
model: nextCandidate.model,
|
|
1816
|
+
vendorId: nextCandidate.vendorId,
|
|
1817
|
+
driverClass: nextCandidate.driverClass,
|
|
1818
|
+
apiName: nextCandidate.apiName,
|
|
1819
|
+
supportsEffortLevel: nextCandidate.supportsEffortLevel || false
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1657
1822
|
getValidationDecisionDescription(finalSuccess, totalAttempts, validationBehavior) {
|
|
1658
1823
|
if (finalSuccess) {
|
|
1659
1824
|
return totalAttempts === 1
|
|
@@ -1816,7 +1981,8 @@ class AIPromptRunner {
|
|
|
1816
1981
|
rawOutput: rawOutput?.substring(0, 200),
|
|
1817
1982
|
outputType: prompt.OutputType,
|
|
1818
1983
|
parseAttempt: true
|
|
1819
|
-
}
|
|
1984
|
+
},
|
|
1985
|
+
maxErrorLength: params?.maxErrorLength
|
|
1820
1986
|
});
|
|
1821
1987
|
const validationResult = new global_1.ValidationResult();
|
|
1822
1988
|
validationResult.Success = false;
|
|
@@ -1834,7 +2000,8 @@ class AIPromptRunner {
|
|
|
1834
2000
|
metadata: {
|
|
1835
2001
|
validationPath: error.dataPath,
|
|
1836
2002
|
validationMessage: error.message
|
|
1837
|
-
}
|
|
2003
|
+
},
|
|
2004
|
+
maxErrorLength: params?.maxErrorLength
|
|
1838
2005
|
});
|
|
1839
2006
|
return { result: modelResult.data?.choices?.[0]?.message?.content, validationResult, validationErrors: validationResult.Errors };
|
|
1840
2007
|
case 'None':
|
|
@@ -1914,7 +2081,8 @@ class AIPromptRunner {
|
|
|
1914
2081
|
metadata: {
|
|
1915
2082
|
originalError: originalError.message,
|
|
1916
2083
|
rawOutput: rawOutput.substring(0, 500)
|
|
1917
|
-
}
|
|
2084
|
+
},
|
|
2085
|
+
maxErrorLength: params.maxErrorLength
|
|
1918
2086
|
});
|
|
1919
2087
|
throw new Error(`JSON repair skipped: raw output does not contain JSON-like characters. Original error: ${originalError.message}`);
|
|
1920
2088
|
}
|
|
@@ -1930,7 +2098,8 @@ class AIPromptRunner {
|
|
|
1930
2098
|
metadata: {
|
|
1931
2099
|
originalError: originalError.message,
|
|
1932
2100
|
rawOutput: rawOutput.substring(0, 500)
|
|
1933
|
-
}
|
|
2101
|
+
},
|
|
2102
|
+
maxErrorLength: params.maxErrorLength
|
|
1934
2103
|
});
|
|
1935
2104
|
}
|
|
1936
2105
|
const json5Result = JSON5.parse(jsonToParse);
|
|
@@ -1973,7 +2142,8 @@ class AIPromptRunner {
|
|
|
1973
2142
|
json5Error: json5Error.message,
|
|
1974
2143
|
aiError: aiRepairError.message,
|
|
1975
2144
|
rawOutput: rawOutput.substring(0, 500)
|
|
1976
|
-
}
|
|
2145
|
+
},
|
|
2146
|
+
maxErrorLength: params.maxErrorLength
|
|
1977
2147
|
});
|
|
1978
2148
|
throw new Error(`JSON repair failed after both JSON5 and AI attempts: ${originalError.message}`);
|
|
1979
2149
|
}
|
|
@@ -2156,11 +2326,22 @@ class AIPromptRunner {
|
|
|
2156
2326
|
}
|
|
2157
2327
|
const saveResult = await promptRun.Save();
|
|
2158
2328
|
if (!saveResult) {
|
|
2159
|
-
|
|
2329
|
+
let errorMsg = 'Unknown error';
|
|
2330
|
+
try {
|
|
2331
|
+
if (promptRun.LatestResult?.Message) {
|
|
2332
|
+
errorMsg = typeof promptRun.LatestResult.Message === 'string'
|
|
2333
|
+
? promptRun.LatestResult.Message
|
|
2334
|
+
: String(promptRun.LatestResult.Message);
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
catch (msgError) {
|
|
2338
|
+
errorMsg = 'Error accessing error message';
|
|
2339
|
+
}
|
|
2340
|
+
this.logError(`Failed to update AIPromptRun with results: ${errorMsg}`, {
|
|
2160
2341
|
category: 'PromptRunUpdate',
|
|
2161
2342
|
metadata: {
|
|
2162
2343
|
promptRunId: promptRun.ID,
|
|
2163
|
-
updateError:
|
|
2344
|
+
updateError: errorMsg
|
|
2164
2345
|
}
|
|
2165
2346
|
});
|
|
2166
2347
|
}
|
|
@@ -2175,13 +2356,12 @@ class AIPromptRunner {
|
|
|
2175
2356
|
}
|
|
2176
2357
|
}
|
|
2177
2358
|
getFailoverConfiguration(prompt) {
|
|
2178
|
-
const promptWithFailover = prompt;
|
|
2179
2359
|
return {
|
|
2180
|
-
strategy:
|
|
2181
|
-
maxAttempts:
|
|
2182
|
-
delaySeconds:
|
|
2183
|
-
modelStrategy:
|
|
2184
|
-
errorScope:
|
|
2360
|
+
strategy: prompt.FailoverStrategy || 'None',
|
|
2361
|
+
maxAttempts: prompt.FailoverMaxAttempts || 3,
|
|
2362
|
+
delaySeconds: prompt.FailoverDelaySeconds || 1,
|
|
2363
|
+
modelStrategy: prompt.FailoverModelStrategy || 'PreferSameModel',
|
|
2364
|
+
errorScope: prompt.FailoverErrorScope || 'All'
|
|
2185
2365
|
};
|
|
2186
2366
|
}
|
|
2187
2367
|
shouldAttemptFailover(error, config, attemptNumber) {
|