{"version":3,"sources":["../../src/model/middleware.ts"],"sourcesContent":["/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { GenkitError, StatusName } from '@genkit-ai/core';\nimport { HasRegistry } from '@genkit-ai/core/registry';\nimport { Document } from '../document.js';\nimport { injectInstructions } from '../formats/index.js';\nimport { ModelArgument } from '../index.js';\nimport type {\n  MediaPart,\n  MessageData,\n  ModelInfo,\n  ModelMiddleware,\n  Part,\n} from '../model.js';\nimport { resolveModel } from '../model.js';\n\n/**\n * Preprocess a GenerateRequest to download referenced http(s) media URLs and\n * inline them as data URIs.\n */\nexport function downloadRequestMedia(options?: {\n  maxBytes?: number;\n  filter?: (part: MediaPart) => boolean;\n}): ModelMiddleware {\n  return async (req, next) => {\n    const { default: fetch } = await import('node-fetch');\n\n    const newReq = {\n      ...req,\n      messages: await Promise.all(\n        req.messages.map(async (message) => {\n          const content: Part[] = await Promise.all(\n            message.content.map(async (part) => {\n              // skip non-media parts and non-http urls, or parts that have been\n              // filtered out by user config\n              if (\n                !part.media ||\n                !part.media.url.startsWith('http') ||\n                (options?.filter && !options?.filter(part))\n              ) {\n                return part;\n              }\n\n              const response = await fetch(part.media.url, {\n                size: options?.maxBytes,\n              });\n              if (response.status !== 200)\n                throw new Error(\n                  `HTTP error downloading media '${\n                    part.media.url\n                  }': ${await response.text()}`\n                );\n\n              // use provided contentType or sniff from response\n              const contentType =\n                part.media.contentType ||\n                response.headers.get('content-type') ||\n                '';\n\n              return {\n                media: {\n                  contentType,\n                  url: `data:${contentType};base64,${Buffer.from(\n                    await response.arrayBuffer()\n                  ).toString('base64')}`,\n                },\n              };\n            })\n          );\n\n          return {\n            ...message,\n            content,\n          };\n        })\n      ),\n    };\n\n    return next(newReq);\n  };\n}\n\n/**\n * Validates that a GenerateRequest does not include unsupported features.\n */\nexport function validateSupport(options: {\n  name: string;\n  supports?: ModelInfo['supports'];\n}): ModelMiddleware {\n  const supports = options.supports || {};\n  return async (req, next) => {\n    function invalid(message: string): never {\n      throw new Error(\n        `Model '${\n          options.name\n        }' does not support ${message}. Request: ${JSON.stringify(\n          req,\n          null,\n          2\n        )}`\n      );\n    }\n\n    if (\n      supports.media === false &&\n      req.messages.some((message) => message.content.some((part) => part.media))\n    )\n      invalid('media, but media was provided');\n    if (supports.tools === false && req.tools?.length)\n      invalid('tool use, but tools were provided');\n    if (supports.multiturn === false && req.messages.length > 1)\n      invalid(`multiple messages, but ${req.messages.length} were provided`);\n    // if (\n    //   typeof supports.output !== 'undefined' &&\n    //   req.output?.format &&\n    //   !supports.output.includes(req.output?.format)\n    // )\n    //   invalid(`requested output format '${req.output?.format}'`);\n    return next();\n  };\n}\n\n// N.B. Figure out why array.findLast isn't available despite setting target\n// to ES2022 (Node 16.14.0)\nfunction lastUserMessage(messages: MessageData[]) {\n  for (let i = messages.length - 1; i >= 0; i--) {\n    if (messages[i].role === 'user') {\n      return messages[i];\n    }\n  }\n  return undefined;\n}\n\n/**\n * Provide a simulated system prompt for models that don't support it natively.\n */\nexport function simulateSystemPrompt(options?: {\n  preface: string;\n  acknowledgement: string;\n}): ModelMiddleware {\n  const preface = options?.preface || 'SYSTEM INSTRUCTIONS:\\n';\n  const acknowledgement = options?.acknowledgement || 'Understood.';\n\n  return (req, next) => {\n    const messages = [...req.messages];\n    for (let i = 0; i < messages.length; i++) {\n      if (req.messages[i].role === 'system') {\n        const systemPrompt = messages[i].content;\n        messages.splice(\n          i,\n          1,\n          { role: 'user', content: [{ text: preface }, ...systemPrompt] },\n          { role: 'model', content: [{ text: acknowledgement }] }\n        );\n        break;\n      }\n    }\n    return next({ ...req, messages });\n  };\n}\n\nexport interface AugmentWithContextOptions {\n  /** Preceding text to place before the rendered context documents. */\n  preface?: string | null;\n  /** A function to render a document into a text part to be included in the message. */\n  itemTemplate?: (d: Document, options?: AugmentWithContextOptions) => string;\n  /** The metadata key to use for citation reference. Pass `null` to provide no citations. */\n  citationKey?: string | null;\n}\n\nexport const CONTEXT_PREFACE =\n  '\\n\\nUse the following information to complete your task:\\n\\n';\nconst CONTEXT_ITEM_TEMPLATE = (\n  d: Document,\n  index: number,\n  options?: AugmentWithContextOptions\n) => {\n  let out = '- ';\n  if (options?.citationKey) {\n    out += `[${d.metadata![options.citationKey]}]: `;\n  } else if (options?.citationKey === undefined) {\n    out += `[${d.metadata?.['ref'] || d.metadata?.['id'] || index}]: `;\n  }\n  out += d.text + '\\n';\n  return out;\n};\n\nexport function augmentWithContext(\n  options?: AugmentWithContextOptions\n): ModelMiddleware {\n  const preface =\n    typeof options?.preface === 'undefined' ? CONTEXT_PREFACE : options.preface;\n  const itemTemplate = options?.itemTemplate || CONTEXT_ITEM_TEMPLATE;\n  return (req, next) => {\n    // if there is no context in the request, no-op\n    if (!req.docs?.length) return next(req);\n    const userMessage = lastUserMessage(req.messages);\n    // if there are no messages, no-op\n    if (!userMessage) return next(req);\n    // if there is already a context part, no-op\n    const contextPartIndex = userMessage?.content.findIndex(\n      (p) => p.metadata?.purpose === 'context'\n    );\n    const contextPart =\n      contextPartIndex >= 0 && userMessage.content[contextPartIndex];\n\n    if (contextPart && !contextPart.metadata?.pending) {\n      return next(req);\n    }\n    let out = `${preface || ''}`;\n    req.docs?.forEach((d, i) => {\n      out += itemTemplate(new Document(d), i, options);\n    });\n    out += '\\n';\n    if (contextPartIndex >= 0) {\n      userMessage.content[contextPartIndex] = {\n        ...contextPart,\n        text: out,\n        metadata: { purpose: 'context' },\n      } as Part;\n    } else {\n      userMessage.content.push({ text: out, metadata: { purpose: 'context' } });\n    }\n\n    return next(req);\n  };\n}\n\n/**\n * Options for the `retry` middleware.\n */\nexport interface RetryOptions {\n  /**\n   * The maximum number of times to retry a failed request.\n   * @default 3\n   */\n  maxRetries?: number;\n  /**\n   * An array of `StatusName` values that should trigger a retry.\n   * @default ['UNAVAILABLE', 'DEADLINE_EXCEEDED', 'RESOURCE_EXHAUSTED', 'ABORTED', 'INTERNAL']\n   */\n  statuses?: StatusName[];\n  /**\n   * The initial delay between retries, in milliseconds.\n   * @default 1000\n   */\n  initialDelayMs?: number;\n  /**\n   * The maximum delay between retries, in milliseconds.\n   * @default 60000\n   */\n  maxDelayMs?: number;\n  /**\n   * The factor by which the delay increases after each retry (exponential backoff).\n   * @default 2\n   */\n  backoffFactor?: number;\n  /**\n   * Whether to disable jitter on the delay. Jitter adds a random factor to the\n   * delay to help prevent a \"thundering herd\" of clients all retrying at the\n   * same time.\n   * @default false\n   */\n  noJitter?: boolean;\n  /**\n   * A callback to be executed on each retry attempt.\n   */\n  onError?: (error: Error, attempt: number) => void;\n}\n\nlet __setTimeout: (\n  callback: (...args: any[]) => void,\n  ms?: number\n) => NodeJS.Timeout = setTimeout;\n\n/**\n * FOR TESTING ONLY.\n * @internal\n */\nexport const TEST_ONLY = {\n  setRetryTimeout(\n    impl: (callback: (...args: any[]) => void, ms?: number) => NodeJS.Timeout\n  ) {\n    __setTimeout = impl;\n  },\n};\n\nconst DEFAULT_RETRY_STATUSES: StatusName[] = [\n  'UNAVAILABLE',\n  'DEADLINE_EXCEEDED',\n  'RESOURCE_EXHAUSTED',\n  'ABORTED',\n  'INTERNAL',\n];\n\nconst DEFAULT_FALLBACK_STATUSES: StatusName[] = [\n  'UNAVAILABLE',\n  'DEADLINE_EXCEEDED',\n  'RESOURCE_EXHAUSTED',\n  'ABORTED',\n  'INTERNAL',\n  'NOT_FOUND',\n  'UNIMPLEMENTED',\n];\n\n/**\n * Creates a middleware that retries requests on specific error statuses.\n *\n * ```ts\n * const { text } = await ai.generate({\n *   model: googleAI.model('gemini-pro-latest'),\n *   prompt: 'You are a helpful AI assistant named Walt, say hello',\n *   use: [\n *     retry({\n *       maxRetries: 2,\n *       initialDelayMs: 1000,\n *       backoffFactor: 2,\n *     }),\n *   ],\n * });\n * ```\n */\nexport function retry(options: RetryOptions = {}): ModelMiddleware {\n  const {\n    maxRetries = 3,\n    statuses = DEFAULT_RETRY_STATUSES,\n    initialDelayMs = 1000,\n    maxDelayMs = 60000,\n    backoffFactor = 2,\n    noJitter = false,\n    onError,\n  } = options;\n\n  return async (req, next) => {\n    let lastError: any;\n    let currentDelay = initialDelayMs;\n    for (let i = 0; i <= maxRetries; i++) {\n      try {\n        return await next(req);\n      } catch (e) {\n        lastError = e;\n        const error = e as Error;\n        if (i < maxRetries) {\n          let shouldRetry = false;\n          if (error instanceof GenkitError) {\n            if (statuses.includes(error.status)) {\n              shouldRetry = true;\n            }\n          } else {\n            shouldRetry = true;\n          }\n\n          if (shouldRetry) {\n            onError?.(error, i + 1);\n            let delay = currentDelay;\n            if (!noJitter) {\n              delay = delay + 1000 * Math.pow(2, i) * Math.random();\n            }\n            await new Promise((resolve) => __setTimeout(resolve, delay));\n            currentDelay = Math.min(currentDelay * backoffFactor, maxDelayMs);\n            continue;\n          }\n        }\n        throw error;\n      }\n    }\n    throw lastError;\n  };\n}\n\n/**\n * Options for the `fallback` middleware.\n */\nexport interface FallbackOptions {\n  /**\n   * An array of models to try in order.\n   */\n  models: ModelArgument[];\n  /**\n   * An array of `StatusName` values that should trigger a fallback.\n   * @default ['UNAVAILABLE', 'DEADLINE_EXCEEDED', 'RESOURCE_EXHAUSTED', 'ABORTED', 'INTERNAL', 'NOT_FOUND', 'UNIMPLEMENTED']\n   */\n  statuses?: StatusName[];\n  /**\n   * A callback to be executed on each fallback attempt.\n   */\n  onError?: (error: Error) => void;\n}\n\n/**\n * Creates a middleware that falls back to a different model on specific error statuses.\n *\n * ```ts\n * const { text } = await ai.generate({\n *   model: googleAI.model('gemini-pro-latest'),\n *   prompt: 'You are a helpful AI assistant named Walt, say hello',\n *   use: [\n *     fallback(ai, {\n *       models: [googleAI.model('gemini-flash-latest')],\n *       statuses: ['RESOURCE_EXHAUSTED'],\n *     }),\n *   ],\n * });\n * ```\n */\nexport function fallback(\n  ai: HasRegistry,\n  options: FallbackOptions\n): ModelMiddleware {\n  const { models, statuses = DEFAULT_FALLBACK_STATUSES, onError } = options;\n\n  return async (req, next) => {\n    try {\n      return await next(req);\n    } catch (e) {\n      if (e instanceof GenkitError && statuses.includes(e.status)) {\n        onError?.(e);\n        let lastError: any = e;\n        for (const model of models) {\n          try {\n            const resolved = await resolveModel(ai.registry, model);\n            return await resolved.modelAction(req);\n          } catch (e2) {\n            lastError = e2;\n            if (e2 instanceof GenkitError && statuses.includes(e2.status)) {\n              onError?.(e2);\n              continue;\n            }\n            throw e2;\n          }\n        }\n        throw lastError;\n      }\n      throw e;\n    }\n  };\n}\n\nexport interface SimulatedConstrainedGenerationOptions {\n  instructionsRenderer?: (schema: Record<string, any>) => string;\n}\n\nconst DEFAULT_CONSTRAINED_GENERATION_INSTRUCTIONS = (\n  schema: Record<string, any>\n) => `Output should be in JSON format and conform to the following schema:\n\n\\`\\`\\`\n${JSON.stringify(schema)}\n\\`\\`\\`\n`;\n\n/**\n * Model middleware that simulates constrained generation by injecting generation\n * instructions into the user message.\n */\nexport function simulateConstrainedGeneration(\n  options?: SimulatedConstrainedGenerationOptions\n): ModelMiddleware {\n  return (req, next) => {\n    let instructions: string | undefined;\n    if (req.output?.constrained && req.output?.schema) {\n      instructions = (\n        options?.instructionsRenderer ??\n        DEFAULT_CONSTRAINED_GENERATION_INSTRUCTIONS\n      )(req.output?.schema);\n\n      req = {\n        ...req,\n        messages: injectInstructions(req.messages, instructions),\n        output: {\n          ...req.output,\n          // we're simulating it, so to the underlying model it's unconstrained.\n          constrained: false,\n          format: undefined,\n          contentType: undefined,\n          schema: undefined,\n        },\n      };\n    }\n\n    return next(req);\n  };\n}\n"],"mappings":"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","names":[]}