{"version":3,"sources":["../src/tts.ts"],"sourcesContent":["/**\n * Copyright 2024 The Fire Company\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 */\nimport type { GenerateRequest, GenerateResponseData, Genkit } from 'genkit';\nimport { GenerationCommonConfigSchema, Message, z } from 'genkit';\nimport type { ModelAction } from 'genkit/model';\nimport { modelRef } from 'genkit/model';\nimport type OpenAI from 'openai';\nimport { type SpeechCreateParams } from 'openai/resources/audio/index.mjs';\n\nexport const TTSConfigSchema = GenerationCommonConfigSchema.extend({\n  voice: z\n    .enum(['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'])\n    .optional()\n    .default('alloy'),\n  speed: z.number().min(0.25).max(4.0).optional(),\n  response_format: z\n    .enum(['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm'])\n    .optional(),\n});\n\nexport const tts1 = modelRef({\n  name: 'openai/tts-1',\n  info: {\n    label: 'OpenAI - Text-to-speech 1',\n    supports: {\n      media: false,\n      output: ['media'],\n      multiturn: false,\n      systemRole: false,\n      tools: false,\n    },\n  },\n  configSchema: TTSConfigSchema,\n});\n\nexport const tts1Hd = modelRef({\n  name: 'openai/tts-1-hd',\n  info: {\n    label: 'OpenAI - Text-to-speech 1 HD',\n    supports: {\n      media: false,\n      output: ['media'],\n      multiturn: false,\n      systemRole: false,\n      tools: false,\n    },\n  },\n  configSchema: TTSConfigSchema,\n});\n\nexport const SUPPORTED_TTS_MODELS = {\n  'tts-1': tts1,\n  'tts-1-hd': tts1Hd,\n};\n\nexport const RESPONSE_FORMAT_MEDIA_TYPES = {\n  mp3: 'audio/mpeg',\n  opus: 'audio/opus',\n  aac: 'audio/aac',\n  flac: 'audio/flac',\n  wav: 'audio/wav',\n  pcm: 'audio/L16',\n};\n\nfunction toTTSRequest(\n  modelName: string,\n  request: GenerateRequest<typeof TTSConfigSchema>\n): SpeechCreateParams {\n  const mappedModelName = request.config?.version || modelName;\n  const options: SpeechCreateParams = {\n    model: mappedModelName,\n    input: new Message(request.messages[0]).text,\n    voice: request.config?.voice ?? 'alloy',\n    speed: request.config?.speed,\n    response_format: request.config?.response_format,\n  };\n  for (const k in options) {\n    if (options[k] === undefined) {\n      delete options[k];\n    }\n  }\n  return options;\n}\n\nfunction toGenerateResponse(\n  result: Buffer,\n  responseFormat: z.infer<typeof TTSConfigSchema>['response_format'] = 'mp3'\n): GenerateResponseData {\n  const mediaType = RESPONSE_FORMAT_MEDIA_TYPES[responseFormat];\n  return {\n    candidates: [\n      {\n        index: 0,\n        finishReason: 'stop',\n        message: {\n          role: 'model',\n          content: [\n            {\n              media: {\n                contentType: mediaType,\n                url: `data:${mediaType};base64,${result.toString('base64')}`,\n              },\n            },\n          ],\n        },\n      },\n    ],\n  };\n}\n\nexport function ttsModel(\n  ai: Genkit,\n  name: string,\n  client: OpenAI\n): ModelAction<typeof TTSConfigSchema> {\n  const modelId = `openai/${name}`;\n  const model = SUPPORTED_TTS_MODELS[name];\n  if (!model) throw new Error(`Unsupported model: ${name}`);\n\n  return ai.defineModel<typeof TTSConfigSchema>(\n    {\n      name: modelId,\n      ...model.info,\n      configSchema: model.configSchema,\n    },\n    async (request) => {\n      const ttsRequest = toTTSRequest(name, request);\n      const result = await client.audio.speech.create(ttsRequest);\n      const resultArrayBuffer = await result.arrayBuffer();\n      const resultBuffer = Buffer.from(new Uint8Array(resultArrayBuffer));\n      return toGenerateResponse(resultBuffer, ttsRequest.response_format);\n    }\n  );\n}\n"],"mappings":";;;;;AAgBA,SAAS,8BAA8B,SAAS,SAAS;AAEzD,SAAS,gBAAgB;AAIlB,MAAM,kBAAkB,6BAA6B,OAAO;AAAA,EACjE,OAAO,EACJ,KAAK,CAAC,SAAS,QAAQ,SAAS,QAAQ,QAAQ,SAAS,CAAC,EAC1D,SAAS,EACT,QAAQ,OAAO;AAAA,EAClB,OAAO,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,IAAI,CAAG,EAAE,SAAS;AAAA,EAC9C,iBAAiB,EACd,KAAK,CAAC,OAAO,QAAQ,OAAO,QAAQ,OAAO,KAAK,CAAC,EACjD,SAAS;AACd,CAAC;AAEM,MAAM,OAAO,SAAS;AAAA,EAC3B,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,UAAU;AAAA,MACR,OAAO;AAAA,MACP,QAAQ,CAAC,OAAO;AAAA,MAChB,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,cAAc;AAChB,CAAC;AAEM,MAAM,SAAS,SAAS;AAAA,EAC7B,MAAM;AAAA,EACN,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,UAAU;AAAA,MACR,OAAO;AAAA,MACP,QAAQ,CAAC,OAAO;AAAA,MAChB,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EACA,cAAc;AAChB,CAAC;AAEM,MAAM,uBAAuB;AAAA,EAClC,SAAS;AAAA,EACT,YAAY;AACd;AAEO,MAAM,8BAA8B;AAAA,EACzC,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,KAAK;AACP;AAEA,SAAS,aACP,WACA,SACoB;AAhFtB;AAiFE,QAAM,oBAAkB,aAAQ,WAAR,mBAAgB,YAAW;AACnD,QAAM,UAA8B;AAAA,IAClC,OAAO;AAAA,IACP,OAAO,IAAI,QAAQ,QAAQ,SAAS,CAAC,CAAC,EAAE;AAAA,IACxC,QAAO,mBAAQ,WAAR,mBAAgB,UAAhB,YAAyB;AAAA,IAChC,QAAO,aAAQ,WAAR,mBAAgB;AAAA,IACvB,kBAAiB,aAAQ,WAAR,mBAAgB;AAAA,EACnC;AACA,aAAW,KAAK,SAAS;AACvB,QAAI,QAAQ,CAAC,MAAM,QAAW;AAC5B,aAAO,QAAQ,CAAC;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,mBACP,QACA,iBAAqE,OAC/C;AACtB,QAAM,YAAY,4BAA4B,cAAc;AAC5D,SAAO;AAAA,IACL,YAAY;AAAA,MACV;AAAA,QACE,OAAO;AAAA,QACP,cAAc;AAAA,QACd,SAAS;AAAA,UACP,MAAM;AAAA,UACN,SAAS;AAAA,YACP;AAAA,cACE,OAAO;AAAA,gBACL,aAAa;AAAA,gBACb,KAAK,QAAQ,SAAS,WAAW,OAAO,SAAS,QAAQ,CAAC;AAAA,cAC5D;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,SACd,IACA,MACA,QACqC;AACrC,QAAM,UAAU,UAAU,IAAI;AAC9B,QAAM,QAAQ,qBAAqB,IAAI;AACvC,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,sBAAsB,IAAI,EAAE;AAExD,SAAO,GAAG;AAAA,IACR;AAAA,MACE,MAAM;AAAA,OACH,MAAM,OAFX;AAAA,MAGE,cAAc,MAAM;AAAA,IACtB;AAAA,IACA,CAAO,YAAY;AACjB,YAAM,aAAa,aAAa,MAAM,OAAO;AAC7C,YAAM,SAAS,MAAM,OAAO,MAAM,OAAO,OAAO,UAAU;AAC1D,YAAM,oBAAoB,MAAM,OAAO,YAAY;AACnD,YAAM,eAAe,OAAO,KAAK,IAAI,WAAW,iBAAiB,CAAC;AAClE,aAAO,mBAAmB,cAAc,WAAW,eAAe;AAAA,IACpE;AAAA,EACF;AACF;","names":[]}