@genkit-ai/compat-oai
Version:
Genkit AI framework plugin for OpenAI APIs.
1 lines • 31.4 kB
Source Map (JSON)
{"version":3,"sources":["../src/model.ts"],"sourcesContent":["/**\n * Copyright 2024 The Fire Company\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 type {\n GenerateRequest,\n GenerateResponseChunkData,\n GenerateResponseData,\n MessageData,\n ModelReference,\n Part,\n Role,\n StreamingCallback,\n ToolRequestPart,\n} from 'genkit';\nimport {\n GenerationCommonConfigSchema,\n GenkitError,\n Message,\n StatusName,\n modelRef,\n z,\n type ErrorResponseMetadata,\n} from 'genkit';\nimport type { ModelAction, ModelInfo, ToolDefinition } from 'genkit/model';\nimport { model } from 'genkit/plugin';\nimport OpenAI, { APIError } from 'openai';\nimport type {\n ChatCompletion,\n ChatCompletionChunk,\n ChatCompletionContentPart,\n ChatCompletionCreateParams,\n ChatCompletionCreateParamsNonStreaming,\n ChatCompletionMessageParam,\n ChatCompletionMessageToolCall,\n ChatCompletionRole,\n ChatCompletionTool,\n CompletionChoice,\n} from 'openai/resources/index.mjs';\nimport { PluginOptions } from './index.mjs';\nimport { maybeCreateRequestScopedOpenAIClient, toModelName } from './utils.mjs';\n\n/**\n * Parses a `Retry-After` header value into milliseconds.\n * Supports delay-seconds and HTTP-date formats (RFC 7231 §7.1.3).\n */\nfunction parseRetryAfterMs(value: string): number | undefined {\n if (!value || !value.trim()) return undefined;\n const seconds = Number(value);\n if (!isNaN(seconds) && seconds >= 0) return seconds * 1000;\n const date = new Date(value);\n if (!isNaN(date.getTime())) return Math.max(0, date.getTime() - Date.now());\n return undefined;\n}\n\nconst VisualDetailLevelSchema = z.enum(['auto', 'low', 'high']).optional();\n\ntype VisualDetailLevel = z.infer<typeof VisualDetailLevelSchema>;\n\nexport type ModelRequestBuilder = (\n req: GenerateRequest,\n params: ChatCompletionCreateParams\n) => void;\n\nexport const ChatCompletionCommonConfigSchema =\n GenerationCommonConfigSchema.extend({\n temperature: z.number().min(0).max(2).optional(),\n frequencyPenalty: z.number().min(-2).max(2).optional(),\n logProbs: z.boolean().optional(),\n presencePenalty: z.number().min(-2).max(2).optional(),\n topLogProbs: z.number().int().min(0).max(20).optional(),\n });\n\nexport function toOpenAIRole(role: Role): ChatCompletionRole {\n switch (role) {\n case 'user':\n return 'user';\n case 'model':\n return 'assistant';\n case 'system':\n return 'system';\n case 'tool':\n return 'tool';\n default:\n throw new Error(`role ${role} doesn't map to an OpenAI role.`);\n }\n}\n\n/**\n * Converts a Genkit ToolDefinition to an OpenAI ChatCompletionTool object.\n * @param tool The Genkit ToolDefinition to convert.\n * @returns The converted OpenAI ChatCompletionTool object.\n */\nexport function toOpenAITool(tool: ToolDefinition): ChatCompletionTool {\n return {\n type: 'function',\n function: {\n name: tool.name,\n parameters: tool.inputSchema !== null ? tool.inputSchema : undefined,\n },\n };\n}\n\n/**\n * Checks if a content type is an image type.\n * @param contentType The content type to check.\n * @returns True if the content type is an image type.\n */\nfunction isImageContentType(contentType?: string): boolean {\n if (!contentType) return false;\n return contentType.startsWith('image/');\n}\n\n/**\n * Extracts the base64 data and content type from a data URL.\n * @param url The data URL to parse.\n * @returns The base64 data and content type, or null if invalid.\n */\nfunction extractDataFromBase64Url(url: string): {\n data: string;\n contentType: string;\n} | null {\n const match = url.match(/^data:([^;]+);base64,(.+)$/);\n return (\n match && {\n contentType: match[1],\n data: match[2],\n }\n );\n}\n\n/**\n * Map of content types to file extensions.\n */\nconst FILE_EXTENSIONS: Record<string, string> = {\n 'application/pdf': 'pdf',\n 'application/msword': 'doc',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':\n 'docx',\n 'text/plain': 'txt',\n 'text/csv': 'csv',\n};\n\n/**\n * Generates a filename from a content type.\n * @param contentType The content type.\n * @returns A filename with appropriate extension.\n */\nfunction generateFilenameFromContentType(contentType: string): string {\n const ext = FILE_EXTENSIONS[contentType] || '';\n return ext ? `file.${ext}` : 'file';\n}\n\n/**\n * Converts a Genkit Part to the corresponding OpenAI ChatCompletionContentPart.\n * @param part The Genkit Part to convert.\n * @param visualDetailLevel The visual detail level to use for media parts.\n * @returns The corresponding OpenAI ChatCompletionContentPart.\n * @throws Error if the part contains unsupported fields for the current message role.\n */\nexport function toOpenAITextAndMedia(\n part: Part,\n visualDetailLevel: VisualDetailLevel\n): ChatCompletionContentPart {\n if (part.text) {\n return {\n type: 'text',\n text: part.text,\n };\n } else if (part.media) {\n // Determine the content type from the media part or data URL\n let contentType = part.media.contentType;\n if (!contentType && part.media.url.startsWith('data:')) {\n const extracted = extractDataFromBase64Url(part.media.url);\n if (extracted) {\n contentType = extracted.contentType;\n }\n }\n\n // Check if this is an image type\n // If no contentType is provided, preserve legacy behavior by treating the media\n // as an image URL (e.g. signed URLs or remote images without metadata)\n if (!contentType || isImageContentType(contentType)) {\n return {\n type: 'image_url',\n image_url: {\n url: part.media.url,\n detail: visualDetailLevel,\n },\n };\n }\n\n // For non-image types (like PDF), use the file type\n // OpenAI expects the full data URL (with data: prefix) in file_data\n if (part.media.url.startsWith('data:')) {\n const extracted = extractDataFromBase64Url(part.media.url);\n if (!extracted) {\n throw Error(\n `Invalid data URL format for media: ${part.media.url.substring(0, 50)}...`\n );\n }\n return {\n type: 'file',\n file: {\n filename: generateFilenameFromContentType(extracted.contentType),\n file_data: part.media.url, // Full data URL with prefix\n },\n } as ChatCompletionContentPart;\n }\n\n // If it's a remote URL with non-image content type, this is not supported\n // for chat completions according to OpenAI docs\n throw Error(\n `File URLs are not supported for chat completions. Only base64-encoded files and image URLs are supported. Content type: ${contentType}`\n );\n }\n throw Error(\n `Unsupported genkit part fields encountered for current message role: ${JSON.stringify(part)}.`\n );\n}\n\n/**\n * Converts a Genkit MessageData array to an OpenAI ChatCompletionMessageParam array.\n * @param messages The Genkit MessageData array to convert.\n * @param visualDetailLevel The visual detail level to use for media parts.\n * @returns The converted OpenAI ChatCompletionMessageParam array.\n */\nexport function toOpenAIMessages(\n messages: MessageData[],\n visualDetailLevel: VisualDetailLevel = 'auto'\n): ChatCompletionMessageParam[] {\n const apiMessages: ChatCompletionMessageParam[] = [];\n for (const message of messages) {\n const msg = new Message(message);\n const role = toOpenAIRole(message.role);\n switch (role) {\n case 'user':\n const content = msg.content.map((part) =>\n toOpenAITextAndMedia(part, visualDetailLevel)\n );\n // Check if we have only text content\n const onlyTextContent = content.some((item) => item.type !== 'text');\n\n // If all items are strings, just add them as text\n if (!onlyTextContent) {\n content.forEach((item) => {\n if (item.type === 'text') {\n apiMessages.push({\n role: role,\n content: item.text,\n });\n }\n });\n } else {\n apiMessages.push({\n role: role,\n content: content,\n });\n }\n break;\n case 'system':\n apiMessages.push({\n role: role,\n content: msg.text,\n });\n break;\n case 'assistant': {\n const toolCalls: ChatCompletionMessageToolCall[] = msg.content\n .filter(\n (\n part\n ): part is Part & {\n toolRequest: NonNullable<Part['toolRequest']>;\n } => Boolean(part.toolRequest)\n )\n .map((part) => ({\n id: part.toolRequest.ref ?? '',\n type: 'function',\n function: {\n name: part.toolRequest.name,\n arguments: JSON.stringify(part.toolRequest.input),\n },\n }));\n if (toolCalls.length > 0) {\n apiMessages.push({\n role: role,\n tool_calls: toolCalls,\n });\n } else {\n apiMessages.push({\n role: role,\n content: msg.text,\n });\n }\n break;\n }\n case 'tool': {\n const toolResponseParts = msg.toolResponseParts();\n toolResponseParts.map((part) => {\n apiMessages.push({\n role: role,\n tool_call_id: part.toolResponse.ref ?? '',\n content:\n typeof part.toolResponse.output === 'string'\n ? part.toolResponse.output\n : JSON.stringify(part.toolResponse.output),\n });\n });\n break;\n }\n }\n }\n return apiMessages;\n}\n\nconst finishReasonMap: Record<\n // OpenAI Node SDK doesn't support tool_call in the enum, but it is returned from the API\n CompletionChoice['finish_reason'] | 'tool_calls',\n GenerateResponseData['finishReason']\n> = {\n length: 'length',\n stop: 'stop',\n tool_calls: 'stop',\n content_filter: 'blocked',\n};\n\n/**\n * Converts an OpenAI tool call to a Genkit ToolRequestPart.\n * @param toolCall The OpenAI tool call to convert.\n * @returns The converted Genkit ToolRequestPart.\n */\nexport function fromOpenAIToolCall(\n toolCall:\n | ChatCompletionMessageToolCall\n | ChatCompletionChunk.Choice.Delta.ToolCall,\n choice: ChatCompletion.Choice | ChatCompletionChunk.Choice\n): ToolRequestPart {\n if (!toolCall.function) {\n throw Error(\n `Unexpected openAI chunk choice. tool_calls was provided but one or more tool_calls is missing.`\n );\n }\n const f = toolCall.function;\n\n // Only parse arguments when it is a JSON object and the finish reason is tool_calls to avoid parsing errors\n if (choice.finish_reason === 'tool_calls') {\n return {\n toolRequest: {\n name: f.name!,\n ref: toolCall.id,\n input: f.arguments ? JSON.parse(f.arguments) : f.arguments,\n },\n };\n } else {\n return {\n toolRequest: {\n name: f.name!,\n ref: toolCall.id,\n input: '',\n },\n };\n }\n}\n\n/**\n * Converts an OpenAI message event to a Genkit GenerateResponseData object.\n * @param choice The OpenAI message event to convert.\n * @param jsonMode Whether the event is a JSON response.\n * @returns The converted Genkit GenerateResponseData object.\n */\nexport function fromOpenAIChoice(\n choice: ChatCompletion.Choice,\n jsonMode = false\n): GenerateResponseData {\n const toolRequestParts = choice.message.tool_calls?.map((toolCall) =>\n fromOpenAIToolCall(toolCall, choice)\n );\n\n // Build content array based on what's present in the message\n let content: Part[] = [];\n\n if (toolRequestParts && toolRequestParts.length > 0) {\n content = toolRequestParts as ToolRequestPart[];\n } else {\n // Handle reasoning_content if present\n if (\n 'reasoning_content' in choice.message &&\n choice.message.reasoning_content\n ) {\n content.push({ reasoning: choice.message.reasoning_content as string });\n }\n\n // Handle regular content if present\n if (choice.message.content) {\n content.push(\n jsonMode\n ? { data: JSON.parse(choice.message.content!) }\n : { text: choice.message.content! }\n );\n }\n }\n\n return {\n finishReason: finishReasonMap[choice.finish_reason] || 'other',\n message: {\n role: 'model',\n content,\n },\n };\n}\n\n/**\n * Converts an OpenAI message stream event to a Genkit GenerateResponseData\n * object.\n * @param choice The OpenAI message stream event to convert.\n * @param jsonMode Whether the event is a JSON response.\n * @returns The converted Genkit GenerateResponseData object.\n */\nexport function fromOpenAIChunkChoice(\n choice: ChatCompletionChunk.Choice,\n jsonMode = false\n): GenerateResponseData {\n const toolRequestParts = choice.delta.tool_calls?.map((toolCall) =>\n fromOpenAIToolCall(toolCall, choice)\n );\n\n // Build content array based on what's present in the delta\n let content: Part[] = [];\n\n if (toolRequestParts && toolRequestParts.length > 0) {\n content = toolRequestParts as ToolRequestPart[];\n } else {\n // Handle reasoning_content if present\n if ('reasoning_content' in choice.delta && choice.delta.reasoning_content) {\n content.push({ reasoning: choice.delta.reasoning_content as string });\n }\n\n // Handle regular content if present\n if (choice.delta.content) {\n content.push(\n jsonMode\n ? { data: JSON.parse(choice.delta.content!) }\n : { text: choice.delta.content! }\n );\n }\n }\n\n return {\n finishReason: choice.finish_reason\n ? finishReasonMap[choice.finish_reason] || 'other'\n : 'unknown',\n message: {\n role: 'model',\n content,\n },\n };\n}\n\n/**\n * Converts an OpenAI request to an OpenAI API request body.\n * @param modelName The name of the OpenAI model to use.\n * @param request The Genkit GenerateRequest to convert.\n * @returns The converted OpenAI API request body.\n * @throws An error if the specified model is not supported or if an unsupported output format is requested.\n */\nexport function toOpenAIRequestBody(\n modelName: string,\n request: GenerateRequest,\n requestBuilder?: ModelRequestBuilder\n) {\n const messages = toOpenAIMessages(\n request.messages,\n request.config?.visualDetailLevel\n );\n const {\n temperature,\n maxOutputTokens, // unused\n topK, // unused\n topP: top_p,\n frequencyPenalty: frequency_penalty,\n logProbs: logprobs,\n presencePenalty: presence_penalty,\n topLogProbs: top_logprobs,\n stopSequences: stop,\n version: modelVersion,\n tools: toolsFromConfig,\n apiKey,\n ...restOfConfig\n } = request.config ?? {};\n\n const tools: ChatCompletionTool[] = request.tools?.map(toOpenAITool) ?? [];\n if (toolsFromConfig) {\n tools.push(...(toolsFromConfig as any[]));\n }\n let body = {\n model: modelVersion ?? modelName,\n messages,\n tools: tools.length > 0 ? tools : undefined,\n temperature,\n top_p,\n stop,\n frequency_penalty,\n presence_penalty,\n top_logprobs,\n logprobs,\n } as ChatCompletionCreateParamsNonStreaming;\n if (requestBuilder) {\n // If override provided, apply the override to the OpenAI request.\n // User must control passthrough config too.\n requestBuilder(request, body);\n } else {\n body = { ...body, ...restOfConfig }; // passthrough for other config\n }\n const response_format = request.output?.format;\n if (response_format === 'json') {\n if (request.output?.schema) {\n body.response_format = {\n type: 'json_schema',\n json_schema: {\n name: 'output',\n schema: request.output!.schema,\n },\n };\n } else {\n body.response_format = {\n type: 'json_object',\n };\n }\n } else if (response_format === 'text') {\n body.response_format = {\n type: 'text',\n };\n }\n for (const key in body) {\n if (!body[key] || (Array.isArray(body[key]) && !body[key].length))\n delete body[key];\n }\n return body;\n}\n\n/**\n * Creates the runner used by Genkit to interact with an OpenAI compatible\n * model.\n * @param name The name of the GPT model.\n * @param client The OpenAI client instance.\n * @returns The runner that Genkit will call when the model is invoked.\n */\nexport function openAIModelRunner(\n name: string,\n defaultClient: OpenAI,\n requestBuilder?: ModelRequestBuilder,\n pluginOptions?: Omit<PluginOptions, 'apiKey'>\n) {\n return async (\n request: GenerateRequest,\n options?: {\n streamingRequested?: boolean;\n sendChunk?: StreamingCallback<GenerateResponseChunkData>;\n abortSignal?: AbortSignal;\n }\n ): Promise<GenerateResponseData> => {\n const client = maybeCreateRequestScopedOpenAIClient(\n pluginOptions,\n request,\n defaultClient\n );\n try {\n let response: ChatCompletion;\n const body = toOpenAIRequestBody(name, request, requestBuilder);\n if (options?.streamingRequested) {\n const stream = client.beta.chat.completions.stream(\n {\n ...body,\n stream: true,\n stream_options: {\n include_usage: true,\n },\n },\n { signal: options?.abortSignal }\n );\n for await (const chunk of stream) {\n chunk.choices?.forEach((chunk) => {\n const c = fromOpenAIChunkChoice(chunk);\n options?.sendChunk!({\n index: chunk.index,\n content: c.message?.content ?? [],\n });\n });\n }\n response = await stream.finalChatCompletion();\n } else {\n response = await client.chat.completions.create(body, {\n signal: options?.abortSignal,\n });\n }\n const standardResponse: GenerateResponseData = {\n usage: {\n inputTokens: response.usage?.prompt_tokens,\n outputTokens: response.usage?.completion_tokens,\n totalTokens: response.usage?.total_tokens,\n },\n raw: response,\n };\n if (response.choices.length === 0) {\n return standardResponse;\n } else {\n const choice = response.choices[0];\n return {\n ...fromOpenAIChoice(choice, request.output?.format === 'json'),\n ...standardResponse,\n };\n }\n } catch (e) {\n if (e instanceof APIError) {\n let status: StatusName = 'UNKNOWN';\n switch (e.status) {\n case 429:\n status = 'RESOURCE_EXHAUSTED';\n break;\n case 401:\n status = 'PERMISSION_DENIED';\n break;\n case 403:\n status = 'UNAUTHENTICATED';\n break;\n case 400:\n status = 'INVALID_ARGUMENT';\n break;\n case 500:\n status = 'INTERNAL';\n break;\n case 503:\n status = 'UNAVAILABLE';\n break;\n }\n const retryAfterHeader =\n e.headers?.get?.('retry-after') ??\n (e.headers as any)?.['retry-after'];\n const retryAfterMs = retryAfterHeader\n ? parseRetryAfterMs(retryAfterHeader)\n : undefined;\n const responseMetadata: ErrorResponseMetadata | undefined =\n retryAfterMs !== undefined ? { retryAfterMs } : undefined;\n throw new GenkitError({\n status,\n message: e.message,\n responseMetadata,\n });\n }\n throw e;\n }\n };\n}\n\n/**\n * Method to define a new Genkit Model that is compatible with Open AI\n * Chat Completions API.\n *\n * These models are to be used to chat with a large language model.\n *\n * @param params An object containing parameters for defining the OpenAI\n * Chat model.\n * @param params.ai The Genkit AI instance.\n * @param params.name The name of the model.\n * @param params.client The OpenAI client instance.\n * @param params.modelRef Optional reference to the model's configuration and\n * custom options.\n\n * @returns the created {@link ModelAction}\n */\nexport function defineCompatOpenAIModel<\n CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,\n>(params: {\n name: string;\n client: OpenAI;\n modelRef?: ModelReference<CustomOptions>;\n requestBuilder?: ModelRequestBuilder;\n pluginOptions?: PluginOptions;\n}): ModelAction {\n const { name, client, pluginOptions, modelRef, requestBuilder } = params;\n const modelName = toModelName(name, pluginOptions?.name);\n const actionName =\n modelRef?.name ?? `${pluginOptions?.name ?? 'compat-oai'}/${modelName}`;\n\n return model(\n {\n name: actionName,\n ...modelRef?.info,\n configSchema: modelRef?.configSchema,\n },\n openAIModelRunner(modelName, client, requestBuilder, pluginOptions)\n );\n}\n\nconst GENERIC_MODEL_INFO: ModelInfo = {\n supports: {\n multiturn: true,\n media: true,\n tools: true,\n toolChoice: true,\n systemRole: true,\n },\n};\n\n/** ModelRef helper, with reasonable defaults for OpenAI-compatible providers */\nexport function compatOaiModelRef<\n CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,\n>(params: {\n name: string;\n info?: ModelInfo;\n configSchema?: CustomOptions;\n config?: any;\n namespace?: string;\n}): ModelReference<CustomOptions> {\n const {\n name,\n info = GENERIC_MODEL_INFO,\n configSchema,\n config = undefined,\n namespace,\n } = params;\n return modelRef({\n name,\n configSchema: configSchema || (ChatCompletionCommonConfigSchema as any),\n info: info,\n config,\n namespace,\n });\n}\n"],"mappings":"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","names":["chunk","modelRef"]}