import { ElevenLabsApiError, getElevenLabsApi as getElevenLabsContractApi, toElevenLabs, } from "@automate.ax/integration-contracts/elevenlabs" import type { Encodable } from "@automate.ax/codec" import { retryableActionError, terminalActionError, } from "../../automation/actions" export const ELEVENLABS_ACCOUNT = { connections: [{ connectionMethodId: "api-key" }], } as const /** * Creates an ElevenLabs client with action-aware error classification. * * @param secret Connected account secret. */ export function getElevenLabsActionApi(secret: Record) { const api = getElevenLabsContractApi(secret) const request = api.request.bind(api) api.request = async (path, options) => withElevenLabsErrors(() => request(path, options)) const requestFile = api.requestFile.bind(api) api.requestFile = async (path, options) => withElevenLabsErrors(() => requestFile(path, options)) const requestForm = api.requestForm.bind(api) api.requestForm = async (path, form, options) => withElevenLabsErrors(() => requestForm(path, form, options)) const requestFormFile = api.requestFormFile.bind(api) api.requestFormFile = async (path, form, options) => withElevenLabsErrors(() => requestFormFile(path, form, options)) return api } /** * Applies action retry semantics to one provider request. * * @param request Provider request. */ async function withElevenLabsErrors(request: () => Promise) { try { return await request() } catch (error) { throw classifyElevenLabsApiError(error) } } /** * Encodes public action inputs as ElevenLabs multipart fields. * * @param values Action input fields. */ export function createElevenLabsForm( values: Record, ) { const form = new FormData() for (const [name, value] of Object.entries(values)) { if (value === undefined) continue const key = name.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`) if (value instanceof Blob) { form.set(key, value) } else if (typeof value === "object" && value !== null) { form.set(key, JSON.stringify(toElevenLabs(value))) } else { form.set(key, String(value)) } } return form } /** * Maps an ElevenLabs output format to a practical filename extension. * * @param format Provider output format. */ export function extensionForElevenLabsFormat(format: string) { return format.split("_", 1)[0] === "alaw" || format.startsWith("ulaw_") ? "wav" : format.split("_", 1)[0] } /** * Classifies provider failures for durable action execution. * * @param error Provider error. */ function classifyElevenLabsApiError(error: unknown) { if (!(error instanceof ElevenLabsApiError)) return error if (error.status === 429) { return retryableActionError(error, { retryAt: error.retryAfter === undefined ? undefined : new Date(Date.now() + error.retryAfter * 1_000), }) } if (error.status === 409 || error.status === 425) return error return error.status < 500 ? terminalActionError(error) : error }