Member Junction
    Preparing search index...

    Shared machinery for runners that invoke a model and record the call as an MJ: AI Prompt Runs row: candidate model/vendor selection, credential resolution, execution bounds (timeout and cancellation), failover helpers, and the prompt-run save queue.

    AIPromptRunner, the chat runner, is built on it. Subclasses declare the model type they run through BaseModelRunner.RequiredModelType.

    Hierarchy (View Summary)

    Index

    Constructors

    Properties

    _metadata: Metadata
    _promptRunQueue: BaseEntitySaveQueue = ...
    _provider: IMetadataProvider = null

    Accessors

    • get DefaultLogCategory(): string

      Keeps this runner's uncategorized errors logged under AIPromptRunner, as before the base class existed.

      Returns string

    • get DefaultPromptTimeoutMS(): number

      Engine-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.

      Returns number

    • get ParallelCoordinator(): IParallelExecutionCoordinator

      Lazily 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.

      Returns IParallelExecutionCoordinator

    Methods

    • Returns the template data with _NATIVE_TOOL_CALLING and _NATIVE_CONTROL_FLOW set.

      With a decision, both carry the gate's real answer. Without one (a run that declares no tools), they are set to the envelope path's values unless the caller already supplied them. The Loop system prompt reads both unconditionally, so template parameter extraction marks them required, and a run that left them unset failed to render.

      Parameters

      • data: Record<string, unknown>

        The caller's template data, if any

      • decision: NativeToolCallingDecision

        The resolved native tool-calling decision, or null when no tools were declared

      Returns Record<string, unknown>

    • The error for a model call the caller cancelled. It is classified Cancelled — Fatal and not eligible for failover — so ErrorAnalyzer, the failover loop and the validation retries all stop on it rather than treating it as an unknown model error.

      Parameters

      • Optionalmessage: string

        The message to keep, when a driver already reported the cancellation.

      Returns Error & { errorInfo: AIErrorInfo }

    • Builds a unified, ordered list of model-vendor candidates based on all selection criteria. Uses a 3-phase approach to properly handle SelectionStrategy='Specific' with AIPromptModel priorities.

      Phase 1: Handle explicit model ID (highest priority) Phase 2: Check if SelectionStrategy='Specific' with AIPromptModel entries - use ONLY those with AIPromptModel priorities Phase 3: Use general selection strategy (fallback) - blended priorities from legacy behavior

      Parameters

      • prompt: MJAIPromptEntityExtended

        The AI prompt with selection criteria

      • OptionalexplicitModelId: string

        Explicitly specified model ID (highest priority)

      • OptionalconfigurationId: string

        Configuration ID for filtering

      • OptionalpreferredVendorId: string

        Preferred vendor ID

      • Optionalverbose: boolean

      Returns ModelVendorCandidate[]

      Ordered array of model-vendor candidates (highest priority first)

    • Calculates the delay before the next failover attempt.

      Parameters

      • attemptNumber: number

        The current attempt number (1-based)

      • baseDelaySeconds: number

        The base delay in seconds from configuration

      Returns number

      Delay in milliseconds before the next attempt

      Implements exponential backoff with jitter by default. The delay increases exponentially with each attempt and includes random jitter to prevent thundering herd problems. Override this method to implement custom delay logic.

    • Composes the caller-supplied cancellation token with the resolved model-call timeout into a single AbortSignal that bounds one model call. NEITHER bound is discarded:

      • caller token only → the caller's signal is used directly (behavior unchanged)
      • timeout only → an internal controller aborts after the timeout elapses
      • both → an internal controller relays the caller's abort AND fires on timeout; whichever happens first wins
      • neither → 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).

      Parameters

      Returns ExecutionBound

    • Executes an AI prompt with full support for templates, model selection, and validation.

      Type Parameters

      • T = unknown

      Parameters

      Returns Promise<AIPromptRunResult<T>>

      Promise<AIPromptRunResult> The execution result with tracking information

      // 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}`);
      }
    • Finalizes a prompt-run record in memory immediately AND queues a fire-and-forget UPDATE on _promptRunQueue that re-applies the finalized state after any in-flight 'Running' INSERT and its finalizeSave reload complete.

      Applying the completion fields synchronously in memory ensures callers that inspect result.promptRun right after ExecutePrompt (such as BaseAgent.calculateTokenStats() reading TokensPromptRollup, TokensCompletionRollup, TotalCost, and Status) see the finalized values immediately without blocking on the database round-trip, while re-applying the captured snapshot inside _promptRunQueue.Update(promptRun, ...) guarantees an in-flight INSERT reload can never revert the persisted row back to 'Running'.

      In order, it sets: the completion timing (CompletedAt, ExecutionTimeMS); the outcome (Success, and Status = 'Completed' or 'Failed' from it); any pending failover fields; the subclass's result fields (applyResultFields, invoked once); and finally the token rollups and TotalCost.

      Parameters

      • promptRun: MJAIPromptRunEntityExtended
      • success: boolean

        Whether the call succeeded, as the runner judges it (the chat runner also requires its output to pass validation).

      • endTime: Date
      • executionTimeMS: number
      • applyResultFields: (promptRun: MJAIPromptRunEntityExtended) => void

        Sets the runner's own result columns. It can read the timing and outcome already set, and may refine them (for example ErrorDetails or Status = 'Cancelled'). It must be synchronous.

      Returns Promise<void>

    • Awaits all in-flight MJAIPromptRunEntityExtended INSERT and UPDATE operations queued on this runner's internal BaseEntitySaveQueue (_promptRunQueue) and clears the in-flight parent lookup table.

      Normal intra-step prompt execution is non-blocking (fire-and-forget); callers that own the outer lifecycle (such as BaseAgent at run completion, failure, cancellation, or compaction) call FlushPendingSaves() to guarantee every AIPromptRun row is durably settled before returning.

      Returns Promise<void>

    • Resolves 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.

      Parameters

      Returns number

    • Checks if credentials are available for a given model-vendor combination. This is a pre-flight check used during model selection to determine which candidates have valid authentication configured.

      Checks the credential hierarchy:

      1. Per-request override: params.credentialId
      2. PromptModel bindings: AICredentialBinding WHERE BindingType='PromptModel'
      3. ModelVendor bindings: AICredentialBinding WHERE BindingType='ModelVendor'
      4. Vendor bindings: AICredentialBinding WHERE BindingType='Vendor'
      5. Type-based default: Credential.IsDefault=1 matching AIVendor.CredentialTypeID
      6. Legacy: params.apiKeys[] array
      7. Legacy: AI_VENDOR_API_KEY__ environment variables

      Each tier counts only if CredentialScopeAllows the run's scope to use its source: under 'RuntimeOnly' only 1 and 6 count, so a candidate the caller has no key for is unavailable — which is what keeps failover on the caller's keys.

      Parameters

      • driverClass: string

        The driver class name (e.g., 'OpenAILLM')

      • promptId: string

        The prompt ID for looking up AIPromptModel bindings

      • modelId: string

        The model ID for looking up AIPromptModel and AIModelVendor bindings

      • vendorId: string

        The vendor ID for looking up AIModelVendor and AIVendor bindings

      • Optionalparams: AIPromptParams

        The prompt execution parameters

      Returns boolean

      true if credentials are available, false otherwise

    • Logs a failover attempt for tracking and debugging.

      Parameters

      • promptId: string

        The ID of the prompt being executed

      • attempt: FailoverAttempt

        The failover attempt details

      • willRetry: boolean

        Whether another attempt will be made

      Returns void

      This method logs detailed information about each failover attempt to help with debugging and monitoring. Override this method to implement custom logging or integrate with external monitoring systems.

    • Render a set of child prompt templates WITHOUT executing anything, returning the rendered text keyed by each child's parent placeholder — exactly what the hierarchical execution path embeds into the parent template.

      Exposed for callers that need a child's rendered text before the run: the loop agent uses it to relocate a volatile specialization into the trailing runtime-state message (see ResolveSpecializationPlacement in @memberjunction/ai-agents) while the system prompt renders a stub in its place. Rendering is deterministic for the same inputs, so a subsequent execution of the same params reproduces the same text.

      Parameters

      • childPrompts: ChildPromptParam[]

        The child prompt params, as they would be passed in AIPromptParams.childPrompts.

      • params: AIPromptParams

        The parent params (context user, data, template data) the children render against.

      • OptionalcancellationToken: AbortSignal

        Optional abort signal.

      Returns Promise<{ renderedTemplates: Record<string, string> }>

    • Resolves credentials for AI model execution using a hierarchical resolution system.

      Resolution priority (highest to lowest):

      1. Per-request override: params.credentialId
      2. Prompt-Model specific: AIPromptModel.CredentialID
      3. Model-Vendor specific: AIModelVendor.CredentialID
      4. Vendor default: AIVendor.CredentialID
      5. Legacy: params.apiKeys[] array
      6. Legacy: AI_VENDOR_API_KEY__ environment variables

      IMPORTANT: When ANY credential ID is found (priorities 1-4), the system uses the Credentials path and ignores legacy methods (priorities 5-6).

      Each tier is consulted only if CredentialScopeAllows the run's AIPromptParams.CredentialScope to use its source. Under 'RuntimeOnly' that leaves priority 1 and the apiKeys entry for this driver class; bindings, the vendor default and the environment are skipped.

      Parameters

      • driverClass: string

        The driver class name (e.g., 'OpenAILLM')

      • promptId: string

        The prompt ID for looking up AIPromptModel credentials

      • modelId: string

        The model ID for looking up AIPromptModel and AIModelVendor credentials

      • vendorId: string

        The vendor ID for looking up AIModelVendor and AIVendor credentials

      • params: AIPromptParams

        The prompt execution parameters containing contextUser and optional credentialId

      Returns Promise<string>

      The API key/configuration string to pass to the LLM constructor

    • The prompt layer of the native tool-calling cascade, with a hierarchical run's child prompt merged over its parent.

      An agent runs its agent type's system prompt as prompt and its own prompt as the first child, so the parent's bag alone describes the agent TYPE, not the agent. Native tool calling is a property of the agent's own prompt, so the child's bag sits between the parent and the selected AIPromptModel row: parent → child → row. The merge is per key, so an explicit value in the child (including false) overrides the parent, and an absent key inherits.

      Parameters

      Returns AIConfigurationSections

      The merged prompt configuration, or null when neither layer sets one

    • The usage to record for one call: the driver's when it reports a quantity (tokens, or units in a measure), otherwise the runner's own count, which keeps any cost the driver gave. With neither, the driver's usage (possibly undefined) is returned, and nothing is invented.

      A runner counts what it can see without the driver: the images returned, or the characters it sent. The driver's figure wins because it is the vendor's own measure.

      Parameters

      • reported: ModelUsage

        The usage the driver returned, if any.

      • counted: ModelUsage

        The runner's own count as ModelUsage.ForMedia, or undefined when it has none.

      Returns ModelUsage

    • Selects candidate models for failover based on the strategy and current failure.

      Parameters

      • currentModel: MJAIModelEntityExtended

        The model that just failed

      • currentVendorId: string

        The vendor ID that just failed

      • strategy: "SameModelDifferentVendor" | "NextBestModel" | "PowerRank" | "None"

        The failover strategy to use

      • modelStrategy: "PreferSameModel" | "PreferDifferentModel" | "RequireSameModel"

        The model selection preference

      • allCandidates: ModelVendorCandidate[]

        All available model-vendor candidates

      • attemptHistory: FailoverAttempt[]

        History of previous failover attempts

      Returns ModelVendorCandidate[]

      Array of candidates sorted by priority (highest first)

      This method implements different strategies for selecting failover candidates:

      • SameModelDifferentVendor: Try the same model with different vendors
      • NextBestModel: Try different models in order of preference
      • PowerRank: Use the global power ranking of models

      Override this method to implement custom candidate selection logic.

    • Determines whether a failover attempt should be made based on the error and configuration.

      Parameters

      • error: Error

        The error that occurred during execution

      • config: FailoverConfiguration

        The failover configuration

      • attemptNumber: number

        The current attempt number (1-based)

      Returns boolean

      True if failover should be attempted, false otherwise

      This method uses the ErrorAnalyzer to classify errors and determine if they are eligible for failover based on the configured error scope. Override this method to implement custom failover decision logic.

    • 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.

      Returns Promise<void>