@memberjunction/ai-prompts 2.77.0 → 2.78.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 CHANGED
@@ -2847,4 +2847,73 @@ WHERE ID = @PromptID;
2847
2847
  5. **Use configuration parameters** for environment-specific settings beyond just model selection
2848
2848
  6. **Assign models explicitly to configurations** to ensure clear separation between environments
2849
2849
 
2850
- For more details on API key management, see the [AI Core API Keys documentation](../Core/README.md#api-key-management).
2850
+ For more details on API key management, see the [AI Core API Keys documentation](../Core/README.md#api-key-management).
2851
+
2852
+ ## Model Selection Tracking (v2.78+)
2853
+
2854
+ The AIPromptRunner now provides comprehensive tracking of model selection decisions through the `modelSelectionInfo` property in `AIPromptRunResult`. This feature helps developers understand:
2855
+
2856
+ - Which models were considered during selection
2857
+ - Why specific models were or weren't available
2858
+ - Which configuration influenced the selection
2859
+ - What selection strategy was used
2860
+
2861
+ ### Enhanced Model Selection Information
2862
+
2863
+ ```typescript
2864
+ const result = await promptRunner.ExecutePrompt(params);
2865
+
2866
+ if (result.modelSelectionInfo) {
2867
+ // Access the configuration that was used
2868
+ const config = result.modelSelectionInfo.aiConfiguration;
2869
+ console.log(`Configuration: ${config?.Name || 'Default'}`);
2870
+
2871
+ // See all models that were considered
2872
+ for (const candidate of result.modelSelectionInfo.modelsConsidered) {
2873
+ console.log(`Model: ${candidate.model.Name}`);
2874
+ console.log(` Vendor: ${candidate.vendor?.Name || 'default'}`);
2875
+ console.log(` Priority: ${candidate.priority}`);
2876
+ console.log(` Available: ${candidate.available}`);
2877
+ if (!candidate.available) {
2878
+ console.log(` Reason: ${candidate.unavailableReason}`);
2879
+ }
2880
+ }
2881
+
2882
+ // Understand the final selection
2883
+ console.log(`Selected: ${result.modelSelectionInfo.modelSelected.Name}`);
2884
+ console.log(`Vendor: ${result.modelSelectionInfo.vendorSelected?.Name}`);
2885
+ console.log(`Reason: ${result.modelSelectionInfo.selectionReason}`);
2886
+ console.log(`Strategy: ${result.modelSelectionInfo.selectionStrategy}`);
2887
+ }
2888
+ ```
2889
+
2890
+ ### Database Storage
2891
+
2892
+ Model selection information is stored in the `AIPromptRun.ModelSelection` field as JSON, containing:
2893
+ - Model and vendor IDs (not full entities)
2894
+ - Configuration ID and name
2895
+ - Array of considered models with their availability status
2896
+ - Selection reason and strategy
2897
+
2898
+ Additional fields track:
2899
+ - `SelectionStrategy`: The strategy used ('Default', 'Specific', 'ByPower')
2900
+ - `ModelPowerRank`: Power rank of the selected model
2901
+ - `Status`: Execution status ('Pending', 'Running', 'Completed', 'Failed', 'Cancelled')
2902
+ - `Cancelled`: Boolean flag for cancellation
2903
+ - `CancellationReason`: Why execution was cancelled
2904
+ - `ErrorDetails`: Detailed error information for failures
2905
+
2906
+ ### Benefits
2907
+
2908
+ - **Debugging**: Understand why a specific model was selected
2909
+ - **Monitoring**: Track which models are being used across prompts
2910
+ - **Optimization**: Identify models that are frequently unavailable
2911
+ - **Compliance**: Audit model selection for regulatory requirements
2912
+
2913
+ ## Version History
2914
+
2915
+ - **2.78.0** - Added model selection tracking with full entity objects in results
2916
+ - **2.77.0** - Enhanced status tracking and cancellation support
2917
+ - **2.76.0** - Added intelligent failover system
2918
+ - **2.75.0** - Introduced dynamic template composition
2919
+ - **2.50.0** - Initial release with core prompt execution
@@ -45,6 +45,7 @@ export declare class AIPromptRunner {
45
45
  model?: AIModelEntityExtended;
46
46
  severity?: 'warning' | 'error' | 'critical';
47
47
  }): void;
48
+ private isInferenceProvider;
48
49
  ExecutePrompt<T = unknown>(params: AIPromptParams): Promise<AIPromptRunResult<T>>;
49
50
  private executeSinglePrompt;
50
51
  private executePromptInParallel;
@@ -53,10 +54,11 @@ export declare class AIPromptRunner {
53
54
  private renderPromptWithChildTemplates;
54
55
  private selectModel;
55
56
  private buildModelVendorCandidates;
57
+ private selectModelWithAPIKeyTracked;
56
58
  private selectModelWithAPIKey;
57
59
  private createPromptRun;
58
60
  private renderPromptTemplate;
59
- protected executeModelWithFailover(model: AIModelEntityExtended, renderedPrompt: string, prompt: AIPromptEntity, params: AIPromptParams, vendorId: string | null, conversationMessages?: ChatMessage[], templateMessageRole?: TemplateMessageRole, cancellationToken?: AbortSignal, allCandidates?: ModelVendorCandidate[], promptRun?: AIPromptRunEntity): Promise<ChatResult>;
61
+ protected executeModelWithFailover(model: AIModelEntityExtended, renderedPrompt: string, prompt: AIPromptEntity, params: AIPromptParams, vendorId: string | null, conversationMessages?: ChatMessage[], templateMessageRole?: TemplateMessageRole, cancellationToken?: AbortSignal, allCandidates?: ModelVendorCandidate[], promptRun?: AIPromptRunEntity, vendorDriverClass?: string, vendorApiName?: string): Promise<ChatResult>;
60
62
  protected buildFailoverCandidates(prompt: AIPromptEntity): Promise<ModelVendorCandidate[]>;
61
63
  protected createCandidatesFromModels(models: AIModelEntityExtended[]): ModelVendorCandidate[];
62
64
  protected updatePromptRunWithFailoverSuccess(promptRun: AIPromptRunEntity, failoverAttempts: FailoverAttempt[], currentModel: AIModelEntityExtended, currentVendorId: string | null): void;
@@ -1 +1 @@
1
- {"version":3,"file":"AIPromptRunner.d.ts","sourceRoot":"","sources":["../src/AIPromptRunner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AIPromptRunner.d.ts","sourceRoot":"","sources":["../src/AIPromptRunner.ts"],"names":[],"mappings":"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"}
@@ -82,13 +82,21 @@ class AIPromptRunner {
82
82
  metadata: Object.keys(metadata).length > 0 ? metadata : undefined
83
83
  });
84
84
  }
85
+ isInferenceProvider(modelVendor) {
86
+ const inferenceProviderType = aiengine_1.AIEngine.Instance.VendorTypeDefinitions.find(vt => vt.Name === 'Inference Provider');
87
+ if (!inferenceProviderType) {
88
+ const modelDeveloperType = aiengine_1.AIEngine.Instance.VendorTypeDefinitions.find(vt => vt.Name === 'Model Developer');
89
+ return modelVendor.TypeID !== modelDeveloperType?.ID;
90
+ }
91
+ return modelVendor.TypeID === inferenceProviderType.ID;
92
+ }
85
93
  async ExecutePrompt(params) {
86
94
  const startTime = new Date();
87
95
  const promptRun = null;
88
96
  if (params.cancellationToken?.aborted) {
89
97
  const result = {
90
98
  success: false,
91
- status: 'cancelled',
99
+ status: 'Cancelled',
92
100
  cancelled: true,
93
101
  cancellationReason: 'user_requested',
94
102
  errorMessage: 'Prompt execution was cancelled before starting',
@@ -111,12 +119,15 @@ class AIPromptRunner {
111
119
  let parentPromptRun;
112
120
  let selectedModel;
113
121
  let childTemplateRenderingResult;
122
+ let modelSelectionInfo;
114
123
  if (params.childPrompts && params.childPrompts.length > 0) {
115
124
  let modelSelectionPrompt = prompt;
116
125
  if (params.modelSelectionPrompt) {
117
126
  modelSelectionPrompt = params.modelSelectionPrompt;
118
127
  }
119
- selectedModel = await this.selectModel(modelSelectionPrompt, params.override?.modelId, params.contextUser, params.configurationId, params.override?.vendorId, params);
128
+ const modelResult = await this.selectModel(modelSelectionPrompt, params.override?.modelId, params.contextUser, params.configurationId, params.override?.vendorId, params);
129
+ selectedModel = modelResult.model;
130
+ modelSelectionInfo = modelResult.selectionInfo;
120
131
  if (!selectedModel) {
121
132
  throw new Error(`No suitable model found for prompt ${modelSelectionPrompt.Name}`);
122
133
  }
@@ -128,7 +139,7 @@ class AIPromptRunner {
128
139
  childTemplateRenderingResult = await this.renderChildPromptTemplates(params.childPrompts, params, params.cancellationToken);
129
140
  renderedPromptText = await this.renderPromptWithChildTemplates(prompt, params, childTemplateRenderingResult.renderedTemplates);
130
141
  }
131
- parentPromptRun = await this.createPromptRun(prompt, selectedModel, params, renderedPromptText, startTime, params.override?.vendorId);
142
+ parentPromptRun = await this.createPromptRun(prompt, selectedModel, params, renderedPromptText, startTime, params.override?.vendorId, modelSelectionInfo);
132
143
  }
133
144
  else if (prompt.TemplateID && (!params.conversationMessages || params.templateMessageRole !== 'none')) {
134
145
  if (params.systemPromptOverride) {
@@ -157,7 +168,9 @@ class AIPromptRunner {
157
168
  modelSelectionPrompt = params.modelSelectionPrompt;
158
169
  this.logStatus(`🎯 Using prompt "${modelSelectionPrompt.Name}" for model selection instead of main prompt`, true, params);
159
170
  }
160
- selectedModel = await this.selectModel(modelSelectionPrompt, params.override?.modelId, params.contextUser, params.configurationId, params.override?.vendorId, params);
171
+ const modelResult = await this.selectModel(modelSelectionPrompt, params.override?.modelId, params.contextUser, params.configurationId, params.override?.vendorId, params);
172
+ selectedModel = modelResult.model;
173
+ modelSelectionInfo = modelResult.selectionInfo;
161
174
  if (!selectedModel) {
162
175
  throw new Error(`No suitable model found for prompt ${modelSelectionPrompt.Name}`);
163
176
  }
@@ -170,6 +183,9 @@ class AIPromptRunner {
170
183
  else {
171
184
  result = await this.executeSinglePrompt(prompt, renderedPromptText, params, startTime, parentPromptRun, selectedModel);
172
185
  }
186
+ if (modelSelectionInfo) {
187
+ result.modelSelectionInfo = modelSelectionInfo;
188
+ }
173
189
  return result;
174
190
  }
175
191
  catch (error) {
@@ -186,6 +202,15 @@ class AIPromptRunner {
186
202
  promptRun.CompletedAt = endTime;
187
203
  promptRun.ExecutionTimeMS = executionTimeMS;
188
204
  promptRun.Result = `ERROR: ${error.message}`;
205
+ if (error.message.includes('cancelled')) {
206
+ promptRun.Status = 'Cancelled';
207
+ promptRun.Cancelled = true;
208
+ promptRun.CancellationReason = 'user_requested';
209
+ }
210
+ else {
211
+ promptRun.Status = 'Failed';
212
+ promptRun.ErrorDetails = error.message;
213
+ }
189
214
  const saveResult = await promptRun.Save();
190
215
  if (!saveResult) {
191
216
  this.logError(`Failed to save error to AIPromptRun: ${promptRun.LatestResult?.Message || 'Unknown error'}`, {
@@ -214,13 +239,20 @@ class AIPromptRunner {
214
239
  throw new Error('Prompt execution was cancelled before model selection');
215
240
  }
216
241
  let selectedModel = existingModel;
242
+ let modelSelectionInfo;
243
+ let vendorDriverClass;
244
+ let vendorApiName;
217
245
  if (!selectedModel) {
218
246
  let modelSelectionPrompt = prompt;
219
247
  if (params.modelSelectionPrompt) {
220
248
  modelSelectionPrompt = params.modelSelectionPrompt;
221
249
  this.logStatus(` Using prompt "${modelSelectionPrompt.Name}" for model selection instead of main prompt`, true, params);
222
250
  }
223
- selectedModel = await this.selectModel(modelSelectionPrompt, params.override?.modelId, params.contextUser, params.configurationId, params.override?.vendorId, params);
251
+ const modelResult = await this.selectModel(modelSelectionPrompt, params.override?.modelId, params.contextUser, params.configurationId, params.override?.vendorId, params);
252
+ selectedModel = modelResult.model;
253
+ vendorDriverClass = modelResult.vendorDriverClass;
254
+ vendorApiName = modelResult.vendorApiName;
255
+ modelSelectionInfo = modelResult.selectionInfo;
224
256
  if (!selectedModel) {
225
257
  throw new Error(`No suitable model found for prompt ${modelSelectionPrompt.Name}`);
226
258
  }
@@ -228,11 +260,11 @@ class AIPromptRunner {
228
260
  if (params.cancellationToken?.aborted) {
229
261
  throw new Error('Prompt execution was cancelled after model selection');
230
262
  }
231
- const promptRun = existingPromptRun || await this.createPromptRun(prompt, selectedModel, params, renderedPromptText, startTime, params.override?.vendorId);
263
+ const promptRun = existingPromptRun || await this.createPromptRun(prompt, selectedModel, params, renderedPromptText, startTime, params.override?.vendorId, modelSelectionInfo);
232
264
  if (params.cancellationToken?.aborted) {
233
265
  throw new Error('Prompt execution was cancelled before model execution');
234
266
  }
235
- const { modelResult, parsedResult, validationAttempts, cumulativeTokens } = await this.executeWithValidationRetries(selectedModel, renderedPromptText, prompt, params, promptRun);
267
+ const { modelResult, parsedResult, validationAttempts, cumulativeTokens } = await this.executeWithValidationRetries(selectedModel, renderedPromptText, prompt, params, promptRun, vendorDriverClass, vendorApiName);
236
268
  const endTime = new Date();
237
269
  const executionTimeMS = endTime.getTime() - startTime.getTime();
238
270
  await this.updatePromptRun(promptRun, prompt, modelResult, parsedResult, endTime, executionTimeMS, validationAttempts, cumulativeTokens);
@@ -309,7 +341,7 @@ class AIPromptRunner {
309
341
  }
310
342
  }
311
343
  }
312
- const consolidatedPromptRun = existingPromptRun || await this.createPromptRun(prompt, selectedResult.task.model, params, renderedPromptText, startTime, params.override?.vendorId);
344
+ const consolidatedPromptRun = existingPromptRun || await this.createPromptRun(prompt, selectedResult.task.model, params, renderedPromptText, startTime, params.override?.vendorId, undefined);
313
345
  const endTime = new Date();
314
346
  consolidatedPromptRun.CompletedAt = endTime;
315
347
  consolidatedPromptRun.ExecutionTimeMS = parallelResult.totalExecutionTimeMS;
@@ -348,6 +380,8 @@ class AIPromptRunner {
348
380
  if (hasCost) {
349
381
  consolidatedPromptRun.TotalCost = totalCost;
350
382
  }
383
+ consolidatedPromptRun.Status = parallelResult.successCount > 0 ? 'Completed' : 'Failed';
384
+ consolidatedPromptRun.WasSelectedResult = true;
351
385
  const saveResult = await consolidatedPromptRun.Save();
352
386
  if (!saveResult) {
353
387
  this.logError(`Failed to save consolidated AIPromptRun: ${consolidatedPromptRun.LatestResult?.Message || 'Unknown error'}`, {
@@ -571,33 +605,115 @@ class AIPromptRunner {
571
605
  }
572
606
  }
573
607
  async selectModel(prompt, explicitModelId, contextUser, configurationId, vendorId, params) {
608
+ let configurationName;
609
+ let configuration;
574
610
  try {
575
611
  await aiengine_1.AIEngine.Instance.Config(false, contextUser);
612
+ let selectionStrategy = 'Default';
613
+ if (explicitModelId) {
614
+ selectionStrategy = 'Specific';
615
+ }
616
+ else if (prompt.SelectionStrategy === 'Specific') {
617
+ selectionStrategy = 'Specific';
618
+ }
619
+ else if (prompt.SelectionStrategy === 'ByPower' || prompt.MinPowerRank != null) {
620
+ selectionStrategy = 'ByPower';
621
+ }
622
+ if (configurationId) {
623
+ configuration = aiengine_1.AIEngine.Instance.Configurations.find(c => c.ID === configurationId);
624
+ configurationName = configuration?.Name;
625
+ }
576
626
  const candidates = this.buildModelVendorCandidates(prompt, explicitModelId, configurationId, vendorId);
627
+ const modelsConsidered = [];
577
628
  if (candidates.length === 0) {
578
629
  this.logError(`No suitable model candidates found for prompt ${prompt.Name}`, {
579
630
  category: 'ModelSelection',
580
631
  prompt: prompt,
581
632
  severity: 'critical'
582
633
  });
583
- return null;
634
+ return {
635
+ model: null,
636
+ selectionInfo: {
637
+ aiConfiguration: configuration,
638
+ modelsConsidered: [],
639
+ modelSelected: undefined,
640
+ selectionReason: 'No suitable model candidates found',
641
+ fallbackUsed: false,
642
+ selectionStrategy
643
+ }
644
+ };
584
645
  }
585
- const selected = await this.selectModelWithAPIKey(candidates, params);
646
+ const { selected, consideredModels } = await this.selectModelWithAPIKeyTracked(candidates, params);
647
+ modelsConsidered.push(...consideredModels);
586
648
  if (!selected) {
587
- return null;
649
+ return {
650
+ model: null,
651
+ vendorDriverClass: undefined,
652
+ vendorApiName: undefined,
653
+ selectionInfo: {
654
+ aiConfiguration: configuration,
655
+ modelsConsidered,
656
+ modelSelected: undefined,
657
+ selectionReason: 'No API keys found for any model-vendor combination',
658
+ fallbackUsed: false,
659
+ selectionStrategy
660
+ }
661
+ };
588
662
  }
589
- const modelWithVendor = selected.model;
590
- modelWithVendor._selectedVendorId = selected.vendorId;
591
- modelWithVendor._selectedDriverClass = selected.driverClass;
592
- modelWithVendor._selectedApiName = selected.apiName;
593
- return modelWithVendor;
663
+ let selectionReason = `Selected ${selected.model.Name} via ${selected.vendorName || 'default vendor'}`;
664
+ if (selected.source === 'explicit') {
665
+ selectionReason = `Explicitly requested model ${selected.model.Name}`;
666
+ }
667
+ else if (selected.source === 'prompt-model') {
668
+ selectionReason = `Selected from prompt-specific models (priority: ${selected.priority})`;
669
+ }
670
+ else if (selected.source === 'model-type') {
671
+ selectionReason = `Selected based on model type filtering`;
672
+ }
673
+ else if (selected.source === 'power-rank') {
674
+ selectionReason = `Selected by power rank (${selected.model.PowerRank || 0})`;
675
+ }
676
+ if (selected.isPreferredVendor) {
677
+ selectionReason += ' using preferred vendor';
678
+ }
679
+ const fallbackUsed = candidates.indexOf(selected) > 0;
680
+ let selectedVendor;
681
+ if (selected.vendorId) {
682
+ selectedVendor = aiengine_1.AIEngine.Instance.Vendors.find(v => v.ID === selected.vendorId);
683
+ }
684
+ return {
685
+ model: selected.model,
686
+ vendorDriverClass: selected.driverClass,
687
+ vendorApiName: selected.apiName,
688
+ selectionInfo: {
689
+ aiConfiguration: configuration,
690
+ modelsConsidered,
691
+ modelSelected: selected.model,
692
+ vendorSelected: selectedVendor,
693
+ selectionReason,
694
+ fallbackUsed,
695
+ selectionStrategy
696
+ }
697
+ };
594
698
  }
595
699
  catch (error) {
596
700
  this.logError(error, {
597
701
  category: 'ModelSelection',
598
702
  prompt: prompt
599
703
  });
600
- return null;
704
+ return {
705
+ model: null,
706
+ vendorDriverClass: undefined,
707
+ vendorApiName: undefined,
708
+ selectionInfo: {
709
+ aiConfiguration: configuration,
710
+ modelsConsidered: [],
711
+ modelSelected: undefined,
712
+ selectionReason: `Error during model selection: ${error.message}`,
713
+ fallbackUsed: false,
714
+ selectionStrategy: 'Default'
715
+ }
716
+ };
601
717
  }
602
718
  }
603
719
  buildModelVendorCandidates(prompt, explicitModelId, configurationId, preferredVendorId) {
@@ -607,7 +723,7 @@ class AIPromptRunner {
607
723
  const createCandidatesForModel = (model, basePriority, source, promptModelPriority) => {
608
724
  const modelCandidates = [];
609
725
  const modelVendors = aiengine_1.AIEngine.Instance.ModelVendors
610
- .filter(mv => mv.ModelID === model.ID && mv.Status === 'Active')
726
+ .filter(mv => mv.ModelID === model.ID && mv.Status === 'Active' && this.isInferenceProvider(mv))
611
727
  .sort((a, b) => b.Priority - a.Priority);
612
728
  if (preferredVendorId) {
613
729
  const preferredVendor = modelVendors.find(mv => mv.VendorID === preferredVendorId);
@@ -685,7 +801,7 @@ class AIPromptRunner {
685
801
  if (model && model.IsActive) {
686
802
  const pmPreferredVendorId = pm.VendorID || preferredVendorId;
687
803
  if (pmPreferredVendorId && pmPreferredVendorId !== preferredVendorId) {
688
- const vendor = aiengine_1.AIEngine.Instance.ModelVendors.find(mv => mv.ModelID === model.ID && mv.VendorID === pmPreferredVendorId && mv.Status === 'Active');
804
+ const vendor = aiengine_1.AIEngine.Instance.ModelVendors.find(mv => mv.ModelID === model.ID && mv.VendorID === pmPreferredVendorId && mv.Status === 'Active' && this.isInferenceProvider(mv));
689
805
  if (vendor) {
690
806
  candidates.push({
691
807
  model,
@@ -711,7 +827,8 @@ class AIPromptRunner {
711
827
  (!preferredVendorName ||
712
828
  aiengine_1.AIEngine.Instance.ModelVendors.some(mv => mv.ModelID === m.ID &&
713
829
  mv.Status === 'Active' &&
714
- mv.Vendor === preferredVendorName)));
830
+ mv.Vendor === preferredVendorName &&
831
+ this.isInferenceProvider(mv))));
715
832
  switch (prompt.SelectionStrategy) {
716
833
  case 'ByPower':
717
834
  switch (prompt.PowerPreference) {
@@ -742,6 +859,52 @@ class AIPromptRunner {
742
859
  candidates.sort((a, b) => b.priority - a.priority);
743
860
  return candidates;
744
861
  }
862
+ async selectModelWithAPIKeyTracked(candidates, params) {
863
+ const checkedDrivers = new Map();
864
+ const consideredModels = [];
865
+ let attemptCount = 0;
866
+ for (const candidate of candidates) {
867
+ attemptCount++;
868
+ let hasKey;
869
+ if (checkedDrivers.has(candidate.driverClass)) {
870
+ hasKey = checkedDrivers.get(candidate.driverClass);
871
+ }
872
+ else {
873
+ const apiKey = (0, ai_1.GetAIAPIKey)(candidate.driverClass);
874
+ hasKey = this.isValidAPIKey(apiKey);
875
+ checkedDrivers.set(candidate.driverClass, hasKey);
876
+ }
877
+ let vendorEntity;
878
+ if (candidate.vendorId) {
879
+ vendorEntity = aiengine_1.AIEngine.Instance.Vendors.find(v => v.ID === candidate.vendorId);
880
+ }
881
+ consideredModels.push({
882
+ model: candidate.model,
883
+ vendor: vendorEntity,
884
+ priority: candidate.priority,
885
+ available: hasKey,
886
+ unavailableReason: hasKey ? undefined : `No API key for driver ${candidate.driverClass}`
887
+ });
888
+ if (hasKey) {
889
+ (0, core_1.LogStatus)(` Selected model ${candidate.model.Name} with ${candidate.vendorName || 'default'} vendor (driver: ${candidate.driverClass})`);
890
+ if (candidate.isPreferredVendor) {
891
+ this.logStatus(` Using preferred vendor${candidate.vendorId ? ` (${candidate.vendorName})` : ''}`, true, params);
892
+ }
893
+ this.logStatus(` Checked ${attemptCount} candidate(s) before finding valid API key`, true, params);
894
+ return { selected: candidate, consideredModels };
895
+ }
896
+ }
897
+ const triedSummary = candidates.slice(0, 5).map(c => `${c.model.Name}/${c.vendorName || 'default'}(${c.driverClass})`).join(', ');
898
+ this.logError(`No API keys found for any model-vendor combination. Tried: ${triedSummary}${candidates.length > 5 ? `... (${candidates.length} total)` : ''}`, {
899
+ category: 'APIKeyValidation',
900
+ severity: 'critical',
901
+ metadata: {
902
+ candidatesChecked: candidates.length,
903
+ modelsChecked: consideredModels.length
904
+ }
905
+ });
906
+ return { selected: null, consideredModels };
907
+ }
745
908
  async selectModelWithAPIKey(candidates, params) {
746
909
  const checkedDrivers = new Map();
747
910
  let attemptCount = 0;
@@ -784,12 +947,42 @@ class AIPromptRunner {
784
947
  });
785
948
  return null;
786
949
  }
787
- async createPromptRun(prompt, model, params, systemPromptText, startTime, vendorId) {
950
+ async createPromptRun(prompt, model, params, systemPromptText, startTime, vendorId, modelSelectionInfo) {
788
951
  const promptRun = await this._metadata.GetEntityObject('MJ: AI Prompt Runs', params.contextUser);
789
952
  try {
790
953
  promptRun.NewRecord();
791
954
  promptRun.PromptID = prompt.ID;
792
955
  promptRun.ModelID = model.ID;
956
+ promptRun.Status = 'Running';
957
+ promptRun.Cancelled = false;
958
+ promptRun.CacheHit = false;
959
+ promptRun.StreamingEnabled = false;
960
+ promptRun.WasSelectedResult = false;
961
+ if (modelSelectionInfo) {
962
+ const dbSelectionInfo = {
963
+ configurationId: modelSelectionInfo.aiConfiguration?.ID,
964
+ configurationName: modelSelectionInfo.aiConfiguration?.Name,
965
+ modelsConsidered: modelSelectionInfo.modelsConsidered.map(mc => ({
966
+ modelId: mc.model.ID,
967
+ modelName: mc.model.Name,
968
+ vendorId: mc.vendor?.ID,
969
+ vendorName: mc.vendor?.Name || 'default',
970
+ priority: mc.priority,
971
+ available: mc.available,
972
+ unavailableReason: mc.unavailableReason
973
+ })),
974
+ modelSelected: modelSelectionInfo.modelSelected?.ID,
975
+ vendorSelected: modelSelectionInfo.vendorSelected?.ID,
976
+ selectionReason: modelSelectionInfo.selectionReason,
977
+ fallbackUsed: modelSelectionInfo.fallbackUsed,
978
+ selectionStrategy: modelSelectionInfo.selectionStrategy
979
+ };
980
+ promptRun.ModelSelection = JSON.stringify(dbSelectionInfo);
981
+ promptRun.SelectionStrategy = modelSelectionInfo.selectionStrategy || 'Default';
982
+ if (model.PowerRank != null) {
983
+ promptRun.ModelPowerRank = model.PowerRank;
984
+ }
985
+ }
793
986
  const promptRunWithFailover = promptRun;
794
987
  promptRunWithFailover.OriginalModelID = model.ID;
795
988
  promptRunWithFailover.OriginalRequestStartTime = startTime;
@@ -806,7 +999,7 @@ class AIPromptRunner {
806
999
  }
807
1000
  else {
808
1001
  const modelVendors = aiengine_1.AIEngine.Instance.ModelVendors
809
- .filter((mv) => mv.ModelID === model.ID && mv.Status === 'Active')
1002
+ .filter((mv) => mv.ModelID === model.ID && mv.Status === 'Active' && this.isInferenceProvider(mv))
810
1003
  .sort((a, b) => b.Priority - a.Priority);
811
1004
  if (modelVendors.length > 0) {
812
1005
  promptRun.VendorID = modelVendors[0].VendorID;
@@ -961,10 +1154,10 @@ class AIPromptRunner {
961
1154
  throw error;
962
1155
  }
963
1156
  }
964
- async executeModelWithFailover(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole = 'system', cancellationToken, allCandidates, promptRun) {
1157
+ async executeModelWithFailover(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole = 'system', cancellationToken, allCandidates, promptRun, vendorDriverClass, vendorApiName) {
965
1158
  const failoverConfig = this.getFailoverConfiguration(prompt);
966
1159
  if (failoverConfig.strategy === 'None') {
967
- return this.executeModel(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole, cancellationToken);
1160
+ return this.executeModel(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole, cancellationToken, vendorDriverClass, vendorApiName);
968
1161
  }
969
1162
  const failoverAttempts = [];
970
1163
  let lastError = null;
@@ -990,7 +1183,7 @@ class AIPromptRunner {
990
1183
  }]
991
1184
  });
992
1185
  }
993
- const result = await this.executeModel(currentModel, renderedPrompt, prompt, params, currentVendorId, conversationMessages, templateMessageRole, cancellationToken);
1186
+ const result = await this.executeModel(currentModel, renderedPrompt, prompt, params, currentVendorId, conversationMessages, templateMessageRole, cancellationToken, vendorDriverClass, vendorApiName);
994
1187
  if (failoverAttempts.length > 0 && promptRun) {
995
1188
  this.updatePromptRunWithFailoverSuccess(promptRun, failoverAttempts, currentModel, currentVendorId);
996
1189
  }
@@ -1023,6 +1216,8 @@ class AIPromptRunner {
1023
1216
  const nextCandidate = nextCandidates[0];
1024
1217
  currentModel = nextCandidate.model;
1025
1218
  currentVendorId = nextCandidate.vendorId;
1219
+ vendorDriverClass = nextCandidate.driverClass;
1220
+ vendorApiName = nextCandidate.apiName;
1026
1221
  this.logFailoverAttempt(prompt.ID, failoverAttempt, true);
1027
1222
  if (attemptNumber < failoverConfig.maxAttempts) {
1028
1223
  const delay = this.calculateFailoverDelay(attemptNumber, failoverConfig.delaySeconds);
@@ -1127,30 +1322,29 @@ class AIPromptRunner {
1127
1322
  data: null
1128
1323
  };
1129
1324
  }
1130
- async executeModel(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole = 'system', cancellationToken) {
1325
+ async executeModel(model, renderedPrompt, prompt, params, vendorId, conversationMessages, templateMessageRole = 'system', cancellationToken, vendorDriverClass, vendorApiName) {
1131
1326
  let driverClass;
1132
1327
  let apiName;
1133
1328
  let llm;
1134
1329
  let chatParams;
1135
1330
  try {
1136
1331
  const verbose = params.verbose === true || (0, core_1.IsVerboseLoggingEnabled)();
1137
- const modelWithVendor = model;
1138
- if (modelWithVendor._selectedDriverClass) {
1139
- driverClass = modelWithVendor._selectedDriverClass;
1140
- apiName = modelWithVendor._selectedApiName;
1141
- delete modelWithVendor._selectedVendorId;
1142
- delete modelWithVendor._selectedDriverClass;
1143
- delete modelWithVendor._selectedApiName;
1332
+ if (vendorDriverClass && vendorApiName) {
1333
+ driverClass = vendorDriverClass;
1334
+ apiName = vendorApiName;
1144
1335
  }
1145
1336
  else {
1146
1337
  driverClass = model.DriverClass;
1147
1338
  apiName = model.APIName;
1148
1339
  if (vendorId) {
1149
- const modelVendor = aiengine_1.AIEngine.Instance.ModelVendors.find((mv) => mv.ModelID === model.ID && mv.VendorID === vendorId && mv.Status === 'Active');
1340
+ const modelVendor = aiengine_1.AIEngine.Instance.ModelVendors.find((mv) => mv.ModelID === model.ID && mv.VendorID === vendorId && mv.Status === 'Active' && this.isInferenceProvider(mv));
1150
1341
  if (modelVendor) {
1151
1342
  driverClass = modelVendor.DriverClass || driverClass;
1152
1343
  apiName = modelVendor.APIName || apiName;
1153
1344
  }
1345
+ else {
1346
+ this.logStatus(`⚠️ Vendor ${vendorId} not found or is not an inference provider for model ${model.Name}, using model defaults`, true, params);
1347
+ }
1154
1348
  }
1155
1349
  }
1156
1350
  let apiKey;
@@ -1296,7 +1490,7 @@ class AIPromptRunner {
1296
1490
  }
1297
1491
  return messages;
1298
1492
  }
1299
- async executeWithValidationRetries(selectedModel, renderedPromptText, prompt, params, promptRun) {
1493
+ async executeWithValidationRetries(selectedModel, renderedPromptText, prompt, params, promptRun, vendorDriverClass, vendorApiName) {
1300
1494
  const validationAttempts = [];
1301
1495
  const maxRetries = Math.max(0, prompt.MaxRetries || 0);
1302
1496
  let lastError = null;
@@ -1312,7 +1506,7 @@ class AIPromptRunner {
1312
1506
  (0, core_1.LogStatus)(` 🔄 Retrying execution due to validation failure, attempt ${attempt + 1}/${maxRetries + 1}`);
1313
1507
  await this.applyRetryDelay(prompt, attempt);
1314
1508
  }
1315
- const modelResult = await this.executeModelWithFailover(selectedModel, renderedPromptText, prompt, params, promptRun.VendorID, params.conversationMessages, params.templateMessageRole || 'system', params.cancellationToken, undefined, promptRun);
1509
+ const modelResult = await this.executeModelWithFailover(selectedModel, renderedPromptText, prompt, params, promptRun.VendorID, params.conversationMessages, params.templateMessageRole || 'system', params.cancellationToken, undefined, promptRun, vendorDriverClass, vendorApiName);
1316
1510
  if (modelResult.data?.usage) {
1317
1511
  cumulativePromptTokens += modelResult.data.usage.promptTokens || 0;
1318
1512
  cumulativeCompletionTokens += modelResult.data.usage.completionTokens || 0;
@@ -1857,6 +2051,15 @@ class AIPromptRunner {
1857
2051
  promptRun.TotalRetryDurationMS = 0;
1858
2052
  }
1859
2053
  promptRun.Success = modelResult.success && (parsedResult.validationResult?.Success !== false);
2054
+ promptRun.Status = promptRun.Success ? 'Completed' : 'Failed';
2055
+ if (!promptRun.Success) {
2056
+ if (!modelResult.success && modelResult.errorMessage) {
2057
+ promptRun.ErrorDetails = modelResult.errorMessage;
2058
+ }
2059
+ else if (parsedResult.validationResult?.Success === false) {
2060
+ promptRun.ErrorDetails = `Validation failed: ${parsedResult.validationResult.Errors?.map(e => e.Message).join(', ')}`;
2061
+ }
2062
+ }
1860
2063
  promptRun.TokensPromptRollup = promptRun.TokensPrompt;
1861
2064
  promptRun.TokensCompletionRollup = promptRun.TokensCompletion;
1862
2065
  promptRun.TokensUsedRollup = promptRun.TokensUsed;