Creates a new parallel execution coordinator with default configuration.
ProtectedDefaultEngine-level default model-call timeout, in milliseconds, applied when the caller supplies no
AIPromptParams.timeoutMS. undefined (the default) means NO implicit bound — a prompt run
with neither a timeout nor a cancellation token stays unbounded, exactly as before, so this
change is behavior-preserving for existing callers.
Subclasses (or a host application's runner subclass) can override this to impose a global safety ceiling on every prompt call.
Access the underlying AIModelRunner for embedding and other non-LLM model calls. Use this when you need tracked embedding execution with AIPromptRun record creation.
ProtectedParallelLazily resolves the parallel execution coordinator.
The coordinator is a SUBCLASS of AIPromptRunner — it inherits executeModel and the rest
of the battle-tested execution path so there is a single source of truth for credential / driver
/ ChatParams / streaming resolution. That subclass relationship means the base cannot statically
new it without a hard circular import, so we resolve it through the ClassFactory instead (the
coordinator self-registers via @RegisterClass(AIPromptRunner, PARALLEL_COORDINATOR_KEY)).
Created once per runner and reused; the runner's Provider override is propagated. Throws a clear
error if the coordinator class was never loaded/registered — otherwise ClassFactory would silently
fall back to a plain AIPromptRunner that lacks the parallel methods.
Optional metadata provider override. Callers should set
instance.Provider = providerToUse before invoking run methods
in multi-provider contexts. Falls back to the global default provider when unset.
ProtectedbuildBuilds failover candidates for a prompt based on available models and type restrictions
ProtectedcalculateCalculates the delay before the next failover attempt.
The current attempt number (1-based)
The base delay in seconds from configuration
Delay in milliseconds before the next attempt
ProtectedcreateCreates model-vendor candidates from a list of models
ProtectedcreateComposes the caller-supplied cancellation token with the resolved model-call timeout into a single AbortSignal that bounds one model call. NEITHER bound is discarded:
Signal is undefined and the call runs unbounded (legacy behavior)Implemented with an AbortController + relay listener rather than AbortSignal.any() so it works
on Node 18 (where AbortSignal.any does not exist — it landed in Node 20.3).
OptionalcancellationToken: AbortSignalProtectedexecuteExecutes the AI model with the rendered prompt.
protected so the parallel coordinator subclass reuses this exact code path — credential
resolution, driver selection, ChatParams construction, prefill, media handling, and streaming
all live here ONCE. Do not duplicate this logic elsewhere.
OptionalconversationMessages: ChatMessage[]OptionalcancellationToken: AbortSignalOptionalvendorDriverClass: stringOptionalvendorApiName: stringOptionalvendorSupportsEffortLevel: booleanOptionalmodelEffortLevel: numberProtectedexecuteExecutes the AI model with failover support
OptionalconversationMessages: ChatMessage[]OptionalcancellationToken: AbortSignalOptionalallCandidates: ModelVendorCandidate[]OptionalpromptRun: MJAIPromptRunEntityExtendedOptionalvendorDriverClass: stringOptionalvendorApiName: stringOptionalvendorSupportsEffortLevel: booleanOptionalmodelEffortLevel: numberOptionalcredentialAvailability: Map<string, boolean>This method wraps the core executeModel functionality with intelligent failover capabilities. It will attempt to execute with different models/vendors according to the configured failover strategy when errors occur.
The method calls several smaller, focused helper methods:
Executes an AI prompt with full support for templates, model selection, and validation.
Parameters for prompt execution
Promise<AIPromptRunResult
// Execute with specific result type
interface AnalysisResult {
sentiment: string;
score: number;
keywords: string[];
}
const result = await promptRunner.ExecutePrompt<AnalysisResult>({
prompt: sentimentPrompt,
data: { text: "Customer feedback text" }
});
if (result.success && result.result) {
// result.result is typed as AnalysisResult
console.log(`Sentiment: ${result.result.sentiment}, Score: ${result.result.score}`);
}
Executes multiple tasks in parallel according to their execution groups.
Tasks are grouped by their executionGroup property and executed sequentially by group. Within each group, tasks are executed in parallel up to the concurrency limit.
Array of execution tasks to process
Optionalconfig: Partial<ParallelExecutionConfig>Optional configuration to override defaults
OptionalparentPromptRunId: stringOptional parent prompt run ID for hierarchical logging
OptionalcancellationToken: AbortSignalOptional cancellation token to abort execution
OptionalprogressCallbacks: ProgressCallbacksInterfaceOptional callbacks for progress tracking
OptionalagentRunId: stringOptional agent run ID to link prompt executions to parent agent run
Promise
ProtectedgetResolves the per-model-call timeout: the caller's AIPromptParams.timeoutMS, else the runner's
DefaultPromptTimeoutMS. Non-positive / non-numeric values mean "no timeout".
The bound is applied PER MODEL CALL (not per prompt execution), which mirrors the parallel
path's existing taskTimeoutMS semantics: each failover candidate / validation retry gets a
fresh budget rather than sharing one wall-clock window.
NOTE (issue #3064): there is deliberately NO prompt-entity source here yet — the AIPrompt
table has no TimeoutMS column today. Once a migration adds one and CodeGen regenerates the
entity, this becomes prompt.TimeoutMS ?? params.timeoutMS ?? this.DefaultPromptTimeoutMS
and every bound below starts honoring the per-prompt configuration with no other change.
ProtectedgetRetrieves failover configuration from the prompt entity.
The AI prompt entity containing failover settings
FailoverConfiguration object with strategy and settings
ProtectedlogHelper method for enhanced error logging with metadata
Optionaloptions: {ProtectedlogLogs a failover attempt for tracking and debugging.
The ID of the prompt being executed
The failover attempt details
Whether another attempt will be made
ProtectedlogInternal logging helper that wraps LogStatusEx with verbose control
The message to log
Whether this is a verbose-only message
Optionalparams: AIPromptParamsOptional prompt parameters for custom verbose check
Selects the best result from multiple parallel execution results.
Uses various strategies to determine which result should be considered the "best" output from the parallel executions.
Array of successful execution results to choose from
Configuration for result selection method
OptionalparentPromptRunId: stringOptional parent prompt run ID for hierarchical logging
OptionalcancellationToken: AbortSignalPromise<ExecutionTaskResult | null> - The selected best result, or null if none suitable
ProtectedselectSelects candidate models for failover based on the strategy and current failure.
The model that just failed
The vendor ID that just failed
The failover strategy to use
The model selection preference
All available model-vendor candidates
History of previous failover attempts
Array of candidates sorted by priority (highest first)
This method implements different strategies for selecting failover candidates:
Override this method to implement custom candidate selection logic.
ProtectedshouldDetermines whether a failover attempt should be made based on the error and configuration.
The error that occurred during execution
The failover configuration
The current attempt number (1-based)
True if failover should be attempted, false otherwise
ProtectedupdateUpdates prompt run with successful failover tracking data
Awaits all in-flight prompt-run saves queued by this runner instance. The normal execution path does NOT call this — prompt-run persistence is intentionally fire-and-forget. Exposed for tests and for callers that need the AIPromptRun rows durably written before proceeding.
Coordinates parallel multi-model prompt execution.
SUBCLASSES AIPromptRunner purely to REUSE its execution primitives — most importantly the
protected executeModel, which owns credential resolution, driver selection, ChatParams construction, prefill, media handling, and streaming. Each parallel task delegates to that ONE method (see executeSingleTask) instead of re-implementing it, so the parallel and single-model paths can never drift. The coordinator only adds task fan-out, grouping, timeouts, result selection, and child-run tracking on top.Registered under the base class with a dedicated key so AIPromptRunner.ParallelCoordinator can instantiate it via the ClassFactory without a static (circular) import.