@memberjunction/ai-prompts 5.48.0 → 5.49.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/dist/AIPromptRunner.d.ts +77 -0
- package/dist/AIPromptRunner.d.ts.map +1 -1
- package/dist/AIPromptRunner.js +150 -24
- package/dist/AIPromptRunner.js.map +1 -1
- package/dist/AIPromptTimeoutError.d.ts +20 -0
- package/dist/AIPromptTimeoutError.d.ts.map +1 -0
- package/dist/AIPromptTimeoutError.js +24 -0
- package/dist/AIPromptTimeoutError.js.map +1 -0
- package/dist/ParallelExecutionCoordinator.d.ts.map +1 -1
- package/dist/ParallelExecutionCoordinator.js +14 -2
- package/dist/ParallelExecutionCoordinator.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
package/dist/AIPromptRunner.d.ts
CHANGED
|
@@ -5,6 +5,24 @@ import { IMetadataProvider } from '@memberjunction/core';
|
|
|
5
5
|
import { MJAIModelEntityExtended, MJAIPromptEntityExtended, MJAIPromptRunEntityExtended } from "@memberjunction/ai-core-plus";
|
|
6
6
|
import { type IParallelExecutionCoordinator } from './ParallelExecution.js';
|
|
7
7
|
import { TemplateMessageRole, AIPromptParams } from '@memberjunction/ai-core-plus';
|
|
8
|
+
/**
|
|
9
|
+
* The composed bound applied to a single model call: the caller's cancellation token (if any) merged
|
|
10
|
+
* with the prompt's configured `AIPrompt.TimeoutMS` (if any).
|
|
11
|
+
*
|
|
12
|
+
* Produced by `AIPromptRunner.createExecutionBound` and consumed by the bounded ChatCompletion race.
|
|
13
|
+
* `Dispose()` MUST be called when the call settles so the timeout timer and abort listener are
|
|
14
|
+
* released.
|
|
15
|
+
*/
|
|
16
|
+
export interface ExecutionBound {
|
|
17
|
+
/** Merged abort signal; `undefined` when there is neither a caller token nor a prompt timeout. */
|
|
18
|
+
Signal?: AbortSignal;
|
|
19
|
+
/** The prompt-configured timeout in ms, when one applies. */
|
|
20
|
+
TimeoutMS?: number;
|
|
21
|
+
/** True once the TIMEOUT (not the caller's token) fired — used to build the right error. */
|
|
22
|
+
TimedOut: () => boolean;
|
|
23
|
+
/** Releases the timer and the caller-token listener. Safe to call multiple times. */
|
|
24
|
+
Dispose: () => void;
|
|
25
|
+
}
|
|
8
26
|
/**
|
|
9
27
|
* Represents a model-vendor pair candidate for execution
|
|
10
28
|
*/
|
|
@@ -481,6 +499,65 @@ export declare class AIPromptRunner {
|
|
|
481
499
|
* all live here ONCE. Do not duplicate this logic elsewhere.
|
|
482
500
|
*/
|
|
483
501
|
protected executeModel(model: MJAIModelEntityExtended, renderedPrompt: string, prompt: MJAIPromptEntityExtended, params: AIPromptParams, vendorId: string | null, conversationMessages?: ChatMessage[], templateMessageRole?: TemplateMessageRole, cancellationToken?: AbortSignal, vendorDriverClass?: string, vendorApiName?: string, vendorSupportsEffortLevel?: boolean, modelEffortLevel?: number): Promise<ChatResult>;
|
|
502
|
+
/**
|
|
503
|
+
* Engine-level default model-call timeout, in milliseconds, applied when the caller supplies no
|
|
504
|
+
* `AIPromptParams.timeoutMS`. `undefined` (the default) means NO implicit bound — a prompt run
|
|
505
|
+
* with neither a timeout nor a cancellation token stays unbounded, exactly as before, so this
|
|
506
|
+
* change is behavior-preserving for existing callers.
|
|
507
|
+
*
|
|
508
|
+
* Subclasses (or a host application's runner subclass) can override this to impose a global
|
|
509
|
+
* safety ceiling on every prompt call.
|
|
510
|
+
*/
|
|
511
|
+
protected get DefaultPromptTimeoutMS(): number | undefined;
|
|
512
|
+
/**
|
|
513
|
+
* Resolves the per-model-call timeout: the caller's `AIPromptParams.timeoutMS`, else the runner's
|
|
514
|
+
* {@link DefaultPromptTimeoutMS}. Non-positive / non-numeric values mean "no timeout".
|
|
515
|
+
*
|
|
516
|
+
* The bound is applied PER MODEL CALL (not per prompt execution), which mirrors the parallel
|
|
517
|
+
* path's existing `taskTimeoutMS` semantics: each failover candidate / validation retry gets a
|
|
518
|
+
* fresh budget rather than sharing one wall-clock window.
|
|
519
|
+
*
|
|
520
|
+
* NOTE (issue #3064): there is deliberately NO prompt-entity source here yet — the `AIPrompt`
|
|
521
|
+
* table has no `TimeoutMS` column today. Once a migration adds one and CodeGen regenerates the
|
|
522
|
+
* entity, this becomes `prompt.TimeoutMS ?? params.timeoutMS ?? this.DefaultPromptTimeoutMS`
|
|
523
|
+
* and every bound below starts honoring the per-prompt configuration with no other change.
|
|
524
|
+
*/
|
|
525
|
+
protected getEffectiveTimeoutMS(params: AIPromptParams): number | undefined;
|
|
526
|
+
/**
|
|
527
|
+
* Composes the caller-supplied cancellation token with the resolved model-call timeout into a
|
|
528
|
+
* single {@link AbortSignal} that bounds one model call. NEITHER bound is discarded:
|
|
529
|
+
*
|
|
530
|
+
* - caller token only → the caller's signal is used directly (behavior unchanged)
|
|
531
|
+
* - timeout only → an internal controller aborts after the timeout elapses
|
|
532
|
+
* - both → an internal controller relays the caller's abort AND fires on timeout;
|
|
533
|
+
* whichever happens first wins
|
|
534
|
+
* - neither → `Signal` is undefined and the call runs unbounded (legacy behavior)
|
|
535
|
+
*
|
|
536
|
+
* Implemented with an AbortController + relay listener rather than `AbortSignal.any()` so it works
|
|
537
|
+
* on Node 18 (where `AbortSignal.any` does not exist — it landed in Node 20.3).
|
|
538
|
+
*/
|
|
539
|
+
protected createExecutionBound(prompt: MJAIPromptEntityExtended, params: AIPromptParams, cancellationToken?: AbortSignal): ExecutionBound;
|
|
540
|
+
/**
|
|
541
|
+
* Runs the model call, racing it against the composed execution bound so a hung provider surfaces
|
|
542
|
+
* as a rejected promise the caller's failover/retry logic can act on.
|
|
543
|
+
*
|
|
544
|
+
* A timeout rejects with a typed {@link AIPromptTimeoutError} (classified by ErrorAnalyzer as a
|
|
545
|
+
* retriable NetworkError); a caller cancellation rejects with the same
|
|
546
|
+
* `'Chat completion was cancelled'` error the previous implementation produced, so cancellation
|
|
547
|
+
* semantics are unchanged.
|
|
548
|
+
*
|
|
549
|
+
* NOTE: `Promise.race` ignores the losing `ChatCompletion()` promise's eventual result, but the
|
|
550
|
+
* underlying request IS torn down — the composed signal is on `ChatParams.cancellationToken`, and
|
|
551
|
+
* all 19 drivers now forward it to their SDK/HTTP layer, so aborting the signal (which is exactly
|
|
552
|
+
* what makes the model call lose the race on a timeout or cancellation) aborts the socket rather
|
|
553
|
+
* than leaving it open until the provider closes it.
|
|
554
|
+
*/
|
|
555
|
+
private runChatCompletionBounded;
|
|
556
|
+
/**
|
|
557
|
+
* Builds the rejection error for an aborted model call — a typed {@link AIPromptTimeoutError} when
|
|
558
|
+
* the prompt's TimeoutMS fired, otherwise the legacy cancellation error.
|
|
559
|
+
*/
|
|
560
|
+
private buildAbortError;
|
|
484
561
|
/**
|
|
485
562
|
* Walks every message in chatParams and rewrites media content blocks
|
|
486
563
|
* (image_url / audio_url / video_url / file_url) into visible text markers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AIPromptRunner.d.ts","sourceRoot":"","sources":["../src/AIPromptRunner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,UAAU,EAAmB,WAAW,EAAqE,MAAM,oBAAoB,CAAC;AACtK,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAqB,iBAAiB,EAAwB,MAAM,8BAA8B,CAAC;AAC1G,OAAO,EAAwG,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAG/J,OAAO,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"AIPromptRunner.d.ts","sourceRoot":"","sources":["../src/AIPromptRunner.ts"],"names":[],"mappings":"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"}
|
package/dist/AIPromptRunner.js
CHANGED
|
@@ -6,10 +6,15 @@ import { CleanJSON, MJGlobal, JSONValidator, ValidationResult, ValidationErrorIn
|
|
|
6
6
|
import { CredentialEngine } from '@memberjunction/credentials';
|
|
7
7
|
import { TemplateEngineServer } from '@memberjunction/templates';
|
|
8
8
|
import { ExecutionPlanner } from './ExecutionPlanner.js';
|
|
9
|
+
import { AIPromptTimeoutError } from './AIPromptTimeoutError.js';
|
|
9
10
|
import { AIEngine } from '@memberjunction/aiengine';
|
|
10
11
|
import { AIEngineBase } from '@memberjunction/ai-engine-base';
|
|
11
12
|
import { SystemPlaceholderManager } from '@memberjunction/ai-core-plus';
|
|
12
|
-
|
|
13
|
+
// json5 is a CJS module: under this package's ESM output its import namespace has no
|
|
14
|
+
// `parse` — only the default export does. `import * as JSON5` made JSON5.parse
|
|
15
|
+
// undefined at runtime ("JSON5.parse is not a function"), silently disabling the
|
|
16
|
+
// local JSON-repair tier and forcing every malformed payload onto the AI-repair path.
|
|
17
|
+
import JSON5 from 'json5';
|
|
13
18
|
/**
|
|
14
19
|
* Best-guess MIME family for a ChatMessage content block type when the block
|
|
15
20
|
* doesn't carry its own mimeType. Used by stripUnsupportedMediaBlocks to
|
|
@@ -615,7 +620,10 @@ export class AIPromptRunner {
|
|
|
615
620
|
errorMessage: error.message,
|
|
616
621
|
promptRun,
|
|
617
622
|
executionTimeMS,
|
|
618
|
-
|
|
623
|
+
// Preserve the original exception on the ChatResult so typed failures (e.g.
|
|
624
|
+
// AIPromptTimeoutError from an exceeded AIPrompt.TimeoutMS) survive to the caller
|
|
625
|
+
// instead of being flattened into a string.
|
|
626
|
+
chatResult: { success: false, errorMessage: error.message, errorInfo, exception: error },
|
|
619
627
|
tokensUsed: 0,
|
|
620
628
|
combinedTokensUsed: 0
|
|
621
629
|
};
|
|
@@ -2573,6 +2581,11 @@ export class AIPromptRunner {
|
|
|
2573
2581
|
let apiName;
|
|
2574
2582
|
let llm;
|
|
2575
2583
|
let chatParams;
|
|
2584
|
+
// Compose the caller's cancellation token (if any) with the resolved model-call timeout (if any)
|
|
2585
|
+
// into a SINGLE signal that bounds this model call. Both bounds always apply — whichever fires
|
|
2586
|
+
// first aborts the call. This is the ONE place the timeout is enforced, so the single-model path
|
|
2587
|
+
// and the parallel path (which delegates here) can never diverge.
|
|
2588
|
+
const executionBound = this.createExecutionBound(prompt, params, cancellationToken);
|
|
2576
2589
|
try {
|
|
2577
2590
|
// Get verbose flag for logging
|
|
2578
2591
|
const verbose = params.verbose === true || IsVerboseLoggingEnabled();
|
|
@@ -2618,7 +2631,10 @@ export class AIPromptRunner {
|
|
|
2618
2631
|
throw new Error(`No API name found for model ${model.Name}. Please ensure the model or its vendor configuration includes an APIName.`);
|
|
2619
2632
|
}
|
|
2620
2633
|
chatParams.model = apiName;
|
|
2621
|
-
|
|
2634
|
+
// Hand the driver the COMPOSED signal (caller token ∪ prompt timeout), not the raw caller
|
|
2635
|
+
// token, so any driver that learns to honor ChatParams.cancellationToken aborts the HTTP
|
|
2636
|
+
// request on timeout too — not just on caller cancellation.
|
|
2637
|
+
chatParams.cancellationToken = executionBound.Signal;
|
|
2622
2638
|
// Apply scalar inference params (prompt defaults overridden by additionalParameters) via the
|
|
2623
2639
|
// shared resolver so ChatParams and the persisted AIPromptRun never drift.
|
|
2624
2640
|
const resolvedParams = this.resolveScalarInferenceParams(prompt, params.additionalParameters);
|
|
@@ -2721,27 +2737,8 @@ export class AIPromptRunner {
|
|
|
2721
2737
|
OnContent: (chunk, isComplete) => onStreaming({ content: chunk, isComplete, modelName: model.Name }),
|
|
2722
2738
|
};
|
|
2723
2739
|
}
|
|
2724
|
-
// Execute the model
|
|
2725
|
-
|
|
2726
|
-
// If cancellation token is provided, wrap the execution to handle cancellation
|
|
2727
|
-
return await Promise.race([
|
|
2728
|
-
llm.ChatCompletion(chatParams),
|
|
2729
|
-
new Promise((_, reject) => {
|
|
2730
|
-
if (cancellationToken.aborted) {
|
|
2731
|
-
reject(new Error('Chat completion was cancelled'));
|
|
2732
|
-
}
|
|
2733
|
-
else {
|
|
2734
|
-
cancellationToken.addEventListener('abort', () => {
|
|
2735
|
-
reject(new Error('Chat completion was cancelled'));
|
|
2736
|
-
});
|
|
2737
|
-
}
|
|
2738
|
-
}),
|
|
2739
|
-
]);
|
|
2740
|
-
}
|
|
2741
|
-
else {
|
|
2742
|
-
// No cancellation token, execute normally
|
|
2743
|
-
return await llm.ChatCompletion(chatParams);
|
|
2744
|
-
}
|
|
2740
|
+
// Execute the model bounded by the composed abort signal (caller cancellation + prompt TimeoutMS)
|
|
2741
|
+
return await this.runChatCompletionBounded(llm, chatParams, executionBound);
|
|
2745
2742
|
}
|
|
2746
2743
|
catch (error) {
|
|
2747
2744
|
const errorInfo = ErrorAnalyzer.analyzeError(error, driverClass);
|
|
@@ -2756,6 +2753,135 @@ export class AIPromptRunner {
|
|
|
2756
2753
|
});
|
|
2757
2754
|
throw error;
|
|
2758
2755
|
}
|
|
2756
|
+
finally {
|
|
2757
|
+
// Always release the timeout timer + abort listener, whether the call succeeded, failed,
|
|
2758
|
+
// timed out, or was cancelled. Without this a long-lived process would accumulate timers.
|
|
2759
|
+
executionBound.Dispose();
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
/**
|
|
2763
|
+
* Engine-level default model-call timeout, in milliseconds, applied when the caller supplies no
|
|
2764
|
+
* `AIPromptParams.timeoutMS`. `undefined` (the default) means NO implicit bound — a prompt run
|
|
2765
|
+
* with neither a timeout nor a cancellation token stays unbounded, exactly as before, so this
|
|
2766
|
+
* change is behavior-preserving for existing callers.
|
|
2767
|
+
*
|
|
2768
|
+
* Subclasses (or a host application's runner subclass) can override this to impose a global
|
|
2769
|
+
* safety ceiling on every prompt call.
|
|
2770
|
+
*/
|
|
2771
|
+
get DefaultPromptTimeoutMS() {
|
|
2772
|
+
return undefined;
|
|
2773
|
+
}
|
|
2774
|
+
/**
|
|
2775
|
+
* Resolves the per-model-call timeout: the caller's `AIPromptParams.timeoutMS`, else the runner's
|
|
2776
|
+
* {@link DefaultPromptTimeoutMS}. Non-positive / non-numeric values mean "no timeout".
|
|
2777
|
+
*
|
|
2778
|
+
* The bound is applied PER MODEL CALL (not per prompt execution), which mirrors the parallel
|
|
2779
|
+
* path's existing `taskTimeoutMS` semantics: each failover candidate / validation retry gets a
|
|
2780
|
+
* fresh budget rather than sharing one wall-clock window.
|
|
2781
|
+
*
|
|
2782
|
+
* NOTE (issue #3064): there is deliberately NO prompt-entity source here yet — the `AIPrompt`
|
|
2783
|
+
* table has no `TimeoutMS` column today. Once a migration adds one and CodeGen regenerates the
|
|
2784
|
+
* entity, this becomes `prompt.TimeoutMS ?? params.timeoutMS ?? this.DefaultPromptTimeoutMS`
|
|
2785
|
+
* and every bound below starts honoring the per-prompt configuration with no other change.
|
|
2786
|
+
*/
|
|
2787
|
+
getEffectiveTimeoutMS(params) {
|
|
2788
|
+
const timeoutMS = params.timeoutMS ?? this.DefaultPromptTimeoutMS;
|
|
2789
|
+
return typeof timeoutMS === 'number' && timeoutMS > 0 ? timeoutMS : undefined;
|
|
2790
|
+
}
|
|
2791
|
+
/**
|
|
2792
|
+
* Composes the caller-supplied cancellation token with the resolved model-call timeout into a
|
|
2793
|
+
* single {@link AbortSignal} that bounds one model call. NEITHER bound is discarded:
|
|
2794
|
+
*
|
|
2795
|
+
* - caller token only → the caller's signal is used directly (behavior unchanged)
|
|
2796
|
+
* - timeout only → an internal controller aborts after the timeout elapses
|
|
2797
|
+
* - both → an internal controller relays the caller's abort AND fires on timeout;
|
|
2798
|
+
* whichever happens first wins
|
|
2799
|
+
* - neither → `Signal` is undefined and the call runs unbounded (legacy behavior)
|
|
2800
|
+
*
|
|
2801
|
+
* Implemented with an AbortController + relay listener rather than `AbortSignal.any()` so it works
|
|
2802
|
+
* on Node 18 (where `AbortSignal.any` does not exist — it landed in Node 20.3).
|
|
2803
|
+
*/
|
|
2804
|
+
createExecutionBound(prompt, params, cancellationToken) {
|
|
2805
|
+
const timeoutMS = this.getEffectiveTimeoutMS(params);
|
|
2806
|
+
if (timeoutMS === undefined) {
|
|
2807
|
+
// No prompt timeout: use the caller's token as-is (or nothing at all).
|
|
2808
|
+
return { Signal: cancellationToken, TimeoutMS: undefined, TimedOut: () => false, Dispose: () => { } };
|
|
2809
|
+
}
|
|
2810
|
+
const controller = new AbortController();
|
|
2811
|
+
let timedOut = false;
|
|
2812
|
+
const relayCallerAbort = () => {
|
|
2813
|
+
if (!controller.signal.aborted) {
|
|
2814
|
+
controller.abort(cancellationToken?.reason ?? 'Chat completion was cancelled');
|
|
2815
|
+
}
|
|
2816
|
+
};
|
|
2817
|
+
if (cancellationToken) {
|
|
2818
|
+
if (cancellationToken.aborted) {
|
|
2819
|
+
relayCallerAbort();
|
|
2820
|
+
}
|
|
2821
|
+
else {
|
|
2822
|
+
cancellationToken.addEventListener('abort', relayCallerAbort, { once: true });
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
const timer = setTimeout(() => {
|
|
2826
|
+
if (!controller.signal.aborted) {
|
|
2827
|
+
timedOut = true;
|
|
2828
|
+
controller.abort(new AIPromptTimeoutError(prompt.Name, timeoutMS));
|
|
2829
|
+
}
|
|
2830
|
+
}, timeoutMS);
|
|
2831
|
+
return {
|
|
2832
|
+
Signal: controller.signal,
|
|
2833
|
+
TimeoutMS: timeoutMS,
|
|
2834
|
+
TimedOut: () => timedOut,
|
|
2835
|
+
Dispose: () => {
|
|
2836
|
+
clearTimeout(timer);
|
|
2837
|
+
cancellationToken?.removeEventListener('abort', relayCallerAbort);
|
|
2838
|
+
},
|
|
2839
|
+
};
|
|
2840
|
+
}
|
|
2841
|
+
/**
|
|
2842
|
+
* Runs the model call, racing it against the composed execution bound so a hung provider surfaces
|
|
2843
|
+
* as a rejected promise the caller's failover/retry logic can act on.
|
|
2844
|
+
*
|
|
2845
|
+
* A timeout rejects with a typed {@link AIPromptTimeoutError} (classified by ErrorAnalyzer as a
|
|
2846
|
+
* retriable NetworkError); a caller cancellation rejects with the same
|
|
2847
|
+
* `'Chat completion was cancelled'` error the previous implementation produced, so cancellation
|
|
2848
|
+
* semantics are unchanged.
|
|
2849
|
+
*
|
|
2850
|
+
* NOTE: `Promise.race` ignores the losing `ChatCompletion()` promise's eventual result, but the
|
|
2851
|
+
* underlying request IS torn down — the composed signal is on `ChatParams.cancellationToken`, and
|
|
2852
|
+
* all 19 drivers now forward it to their SDK/HTTP layer, so aborting the signal (which is exactly
|
|
2853
|
+
* what makes the model call lose the race on a timeout or cancellation) aborts the socket rather
|
|
2854
|
+
* than leaving it open until the provider closes it.
|
|
2855
|
+
*/
|
|
2856
|
+
async runChatCompletionBounded(llm, chatParams, bound) {
|
|
2857
|
+
const signal = bound.Signal;
|
|
2858
|
+
if (!signal) {
|
|
2859
|
+
// Neither a caller token nor a prompt timeout — execute unbounded (legacy behavior).
|
|
2860
|
+
return await llm.ChatCompletion(chatParams);
|
|
2861
|
+
}
|
|
2862
|
+
return await Promise.race([
|
|
2863
|
+
llm.ChatCompletion(chatParams),
|
|
2864
|
+
new Promise((_, reject) => {
|
|
2865
|
+
const fail = () => reject(this.buildAbortError(signal, bound));
|
|
2866
|
+
if (signal.aborted) {
|
|
2867
|
+
fail();
|
|
2868
|
+
}
|
|
2869
|
+
else {
|
|
2870
|
+
signal.addEventListener('abort', fail, { once: true });
|
|
2871
|
+
}
|
|
2872
|
+
}),
|
|
2873
|
+
]);
|
|
2874
|
+
}
|
|
2875
|
+
/**
|
|
2876
|
+
* Builds the rejection error for an aborted model call — a typed {@link AIPromptTimeoutError} when
|
|
2877
|
+
* the prompt's TimeoutMS fired, otherwise the legacy cancellation error.
|
|
2878
|
+
*/
|
|
2879
|
+
buildAbortError(signal, bound) {
|
|
2880
|
+
if (bound.TimedOut()) {
|
|
2881
|
+
const reason = signal.reason;
|
|
2882
|
+
return reason instanceof AIPromptTimeoutError ? reason : new Error('Chat completion timed out');
|
|
2883
|
+
}
|
|
2884
|
+
return new Error('Chat completion was cancelled');
|
|
2759
2885
|
}
|
|
2760
2886
|
/**
|
|
2761
2887
|
* Walks every message in chatParams and rewrites media content blocks
|