UNPKG

@genkit-ai/ai

Version:

Genkit AI framework generative AI APIs.

1 lines 19.9 kB
{"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-2.5-pro'),\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-2.5-pro'),\n * prompt: 'You are a helpful AI assistant named Walt, say hello',\n * use: [\n * fallback(ai, {\n * models: [googleAI.model('gemini-2.5-flash')],\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":[]}