All files / src/core/bindings AIBinding.ts

40% Statements 6/15
0% Branches 0/8
20% Functions 1/5
42.85% Lines 6/14

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 10328x 28x                                                 28x                                             28x   28x   28x                                                                                                  
import { Binding } from '@/core/bindings/Binding'
import { lazySettings } from '@/utils/lazySettings'
import { AISettings } from '@/types/AISettings'
import type { CloudflareEnv } from '@/types/CloudflareEnv'
 
/**
 * Accessor for the Workers AI binding (`ai` in `wrangler.json`).
 *
 * Which model it runs, which gateway it routes through, and which binding it
 * reads are all decided by the configuration layer — this class receives the
 * settled values and has no idea whether they came from
 * the defaults or from
 * `config/AIConfig.ts`.
 *
 * @example
 * ```ts
 * const ai = new AIBinding() // settings resolved from the configuration layer
 * const result = await ai.run(ctx.env, { prompt: 'ping' })
 * ```
 *
 * @class
 *
 * @author Bayu Dwiyan Satria
 * @version 1.0.0
 * @since 1.0.0
 */
export class AIBinding<E extends CloudflareEnv = CloudflareEnv> extends Binding<Ai, E> {
  /**
   * This accessor’s settings, resolved on first read.
   */
  private readonly settings: () => AISettings
 
  /**
   * Model used when a call does not name one.
   */
  private readonly model: string
 
  /**
   * AI Gateway id used to route inference, when configured.
   */
  private readonly gateway?: string
 
  /**
   * Constructs an AIBinding instance.
   *
   * @param settings Resolved settings. Defaults to `resolve('ai')`, so callers
   *   normally construct this with no arguments at all.
   */
  constructor(settings?: AISettings) {
    const settle = lazySettings<AISettings>('ai', settings)
 
    super(() => settle().binding)
 
    this.settings = settle
  }
 
  /**
   * Runs an inference request against a Workers AI model.
   *
   * @param env The Worker environment.
   * @param inputs Model inputs, shaped by the model being called.
   * @param model Model to run. Falls back to the configured model.
   * @returns The model output.
   * @throws {@link MissingBindingError} When the AI binding is absent.
   */
  public async run<TResult = any>(env: E, inputs: any, model?: string): Promise<TResult> {
    const target = model || this.settings().model
    const options = this.settings().gateway ? { gateway: { id: this.settings().gateway } } : undefined
 
    return (await this.resolve(env).run(target as any, inputs, options as any)) as TResult
  }
 
  /**
   * Returns an AI Gateway handle for lower-level gateway operations.
   *
   * @param env The Worker environment.
   * @param gatewayId Gateway id. Falls back to the configured gateway.
   * @returns The AI Gateway instance.
   * @throws Error When no gateway id is supplied and none is configured.
   */
  public gatewayOf(env: E, gatewayId?: string): AiGateway {
    const target = gatewayId || this.settings().gateway
 
    if (!target) {
      throw new Error(`No gateway supplied for AI binding '${this.name}' and no gateway configured.`)
    }
 
    return this.resolve(env).gateway(target)
  }
 
  /**
   * Returns the AI Gateway log id of the most recent inference call.
   *
   * Useful for correlating a response with its gateway log entry.
   *
   * @param env The Worker environment.
   * @returns The log id, or `null` when the last call was not routed through a gateway.
   */
  public logId(env: E): string | null {
    return this.resolve(env).aiGatewayLogId
  }
}