{"version":3,"sources":["../src/translate.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  GenerateResponseData,\n  ModelReference,\n} from 'genkit';\nimport { GenerationCommonConfigSchema, Message, modelRef, z } from 'genkit';\nimport type { ModelAction, ModelInfo } from 'genkit/model';\nimport { model } from 'genkit/plugin';\nimport OpenAI from 'openai';\nimport type {\n  TranslationCreateParams,\n  TranslationCreateResponse,\n} from 'openai/resources/audio/index.mjs';\nimport { PluginOptions } from './index.js';\nimport { maybeCreateRequestScopedOpenAIClient, toModelName } from './utils.js';\n\nexport type TranslationRequestBuilder = (\n  req: GenerateRequest,\n  params: TranslationCreateParams\n) => void;\n\nexport const TRANSLATION_MODEL_INFO: ModelInfo = {\n  supports: {\n    media: true,\n    output: ['text', 'json'],\n    multiturn: false,\n    systemRole: false,\n    tools: false,\n  },\n};\n\nexport const TranslationConfigSchema = GenerationCommonConfigSchema.pick({\n  temperature: true,\n}).extend({\n  response_format: z\n    .enum(['json', 'text', 'srt', 'verbose_json', 'vtt'])\n    .optional(),\n});\n\nexport function toTranslationRequest(\n  modelName: string,\n  request: GenerateRequest,\n  requestBuilder?: TranslationRequestBuilder\n): TranslationCreateParams {\n  const message = new Message(request.messages[0]);\n  const media = message.media;\n  if (!media?.url) {\n    throw new Error('No media found in the request');\n  }\n  const mediaBuffer = Buffer.from(\n    media.url.slice(media.url.indexOf(',') + 1),\n    'base64'\n  );\n  const mediaFile = new File([mediaBuffer], 'input', {\n    type:\n      media.contentType ??\n      media.url.slice('data:'.length, media.url.indexOf(';')),\n  });\n  const {\n    temperature,\n    version: modelVersion,\n    maxOutputTokens,\n    stopSequences,\n    topK,\n    topP,\n    ...restOfConfig\n  } = request.config ?? {};\n\n  let options: TranslationCreateParams = {\n    model: modelVersion ?? modelName,\n    file: mediaFile,\n    prompt: message.text,\n    temperature,\n  };\n  if (requestBuilder) {\n    requestBuilder(request, options);\n  } else {\n    options = {\n      ...options,\n      ...restOfConfig, // passthrough rest of the config\n    };\n  }\n  const outputFormat = request.output?.format as 'json' | 'text' | 'media';\n  const customFormat = request.config?.response_format;\n  if (outputFormat && customFormat) {\n    if (\n      outputFormat === 'json' &&\n      customFormat !== 'json' &&\n      customFormat !== 'verbose_json'\n    ) {\n      throw new Error(\n        `Custom response format ${customFormat} is not compatible with output format ${outputFormat}`\n      );\n    }\n  }\n  if (outputFormat === 'media') {\n    throw new Error(`Output format ${outputFormat} is not supported.`);\n  }\n  options.response_format = customFormat || outputFormat || 'text';\n  for (const k in options) {\n    if (options[k] === undefined) {\n      delete options[k];\n    }\n  }\n  return options;\n}\n\nexport function translationToGenerateResponse(\n  result: TranslationCreateResponse | string\n): GenerateResponseData {\n  return {\n    message: {\n      role: 'model',\n      content: [\n        {\n          text: typeof result === 'string' ? result : result.text,\n        },\n      ],\n    },\n    finishReason: 'stop',\n    raw: result,\n  };\n}\n\n/**\n * Method to define a new Genkit Model that is compatible with Open AI\n * Translation API.\n *\n * These models are to be used to translate audio to text.\n *\n * @param params An object containing parameters for defining the OpenAI\n * translation 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 defineCompatOpenAITranslationModel<\n  CustomOptions extends z.ZodTypeAny = z.ZodTypeAny,\n>(params: {\n  name: string;\n  client: OpenAI;\n  pluginOptions?: PluginOptions;\n  modelRef?: ModelReference<CustomOptions>;\n  requestBuilder?: TranslationRequestBuilder;\n}) {\n  const {\n    name,\n    client: defaultClient,\n    pluginOptions,\n    modelRef,\n    requestBuilder,\n  } = params;\n  const modelName = toModelName(name, pluginOptions?.name);\n  const actionName = `${pluginOptions?.name ?? 'compat-oai'}/${modelName}`;\n\n  return model(\n    {\n      name: actionName,\n      ...modelRef?.info,\n      configSchema: modelRef?.configSchema,\n    },\n    async (request, { abortSignal }) => {\n      const params = toTranslationRequest(modelName, request, requestBuilder);\n      const client = maybeCreateRequestScopedOpenAIClient(\n        pluginOptions,\n        request,\n        defaultClient\n      );\n      const result = await client.audio.translations.create(params, {\n        signal: abortSignal,\n      });\n      return translationToGenerateResponse(result);\n    }\n  );\n}\n\n/** Translation ModelRef helper, with reasonable defaults for\n * OpenAI-compatible providers */\nexport function compatOaiTranslationModelRef<\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}) {\n  const {\n    name,\n    info = TRANSLATION_MODEL_INFO,\n    configSchema,\n    config = undefined,\n    namespace,\n  } = params;\n  return modelRef({\n    name,\n    configSchema: configSchema || (TranslationConfigSchema as any),\n    info,\n    config,\n    namespace,\n  });\n}\n"],"mappings":"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","names":["modelRef","params"]}