@memberjunction/ai-prompts 5.47.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.
@@ -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":"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"}
1
+ {"version":3,"file":"AIPromptRunner.d.ts","sourceRoot":"","sources":["../src/AIPromptRunner.ts"],"names":[],"mappings":"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"}
@@ -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
- import * as JSON5 from 'json5';
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
- chatResult: { success: false, errorMessage: error.message, errorInfo },
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
- chatParams.cancellationToken = cancellationToken;
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 with cancellation support
2725
- if (cancellationToken) {
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