elevenlabs / elevenlabs-js

The official JavaScript (Node) library for ElevenLabs Text to Speech.
https://elevenlabs.io
MIT License
147 stars 15 forks source link

voices.add gives a 422, requesting name even when `name` field is provided #47

Closed bradydowling closed 4 months ago

bradydowling commented 5 months ago

Seems like the SDK call isn't properly providing the field to the API? See the error message a little further down. Here is the full code for my file:

import { ElevenLabsClient } from "elevenlabs";
import { createRequire } from "module";
import arg from "arg";
import fs from "fs";

const customRequire = createRequire(import.meta.url);
const dotenv = customRequire("dotenv");
dotenv.config();

const elevenlabs = new ElevenLabsClient({
  apiKey: process.env.ELEVENLABS_API_KEY,
});

// Define the expected command-line arguments
const args = arg({
  // Types
  "--name": String,
  "--file": String,

  // Aliases
  "-n": "--name",
  "-f": "--file",
});

const name = args["--name"];
const filePath = args["--file"];

const run = async () => {
  if (!name || !filePath) {
    console.error("Please provide a voice name and file path");
    return;
  }
  const fullFilePath = `./input/audio-samples/${filePath}`;
  console.log({ name, fullFilePath });

  try {
    const response = await elevenlabs.voices.add({
      files: [fs.createReadStream(fullFilePath)],
      name,
      description: "Meditation voice created by Brady",
      labels: "brady-test",
    });
    console.log(response);
  } catch (error) {
    console.error("Error: ", error);
  }
};

run();

And here is the full run output and error message:

  prompting-scripts git:(main) ✗ yarn clone --name BradyDowling --file brady.m4a
yarn run v1.22.22
$ tsx scripts/create_eleven_labs_voice.ts --name BradyDowling --file brady.m4a
{
  name: 'BradyDowling',
  fullFilePath: './input/audio-samples/brady.m4a'
}
(node:15998) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
Error:  UnprocessableEntityError: UnprocessableEntityError
Status code: 422
Body: {
  "detail": [
    {
      "loc": [
        "body",
        "name"
      ],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}
    at Voices.<anonymous> (/Users/brady/Dev/prompting-scripts/node_modules/elevenlabs/api/resources/voices/client/Client.js:463:31)
    at Generator.next (<anonymous>)
    at fulfilled (/Users/brady/Dev/prompting-scripts/node_modules/elevenlabs/api/resources/voices/client/Client.js:31:58)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  statusCode: 422,
  body: { detail: [ [Object] ] }
}
✨  Done in 0.65s.

Here's my package.json in case that helps:

{
  "version": "1.0.0",
  "main": "index.ts",
  "author": "Brady Dowling",
  "license": "MIT",
  "type": "module",
  "scripts": {
    "clone": "tsx scripts/create_eleven_labs_voice.ts",
  },
  "dependencies": {
    "arg": "^5.0.2",
    "assemblyai": "^4.4.5",
    "axios": "^1.6.7",
    "cheerio": "^1.0.0-rc.12",
    "compromise": "^14.12.0",
    "dotenv": "^16.4.5",
    "elevenlabs": "^0.6.0",
    "enquirer": "^2.4.1",
    "fast-glob": "^3.3.2",
    "fluent-ffmpeg": "^2.1.2",
    "openai": "^4.28.4",
    "ora": "^8.0.1",
    "playwright": "^1.42.1"
  },
  "devDependencies": {
    "@types/cheerio": "^0.22.35",
    "@types/node": "^20.11.25",
    "ts-node": "^10.9.2",
    "tslib": "^2.6.2",
    "tsx": "^4.10.5",
    "typescript": "^5.4.2"
  }
}

I looked at the call signature of add and I'm matching it. I'm not sure what else I could be doing wrong.

node: v22.2.0 yarn: 1.22.22

bradydowling commented 5 months ago

I dug into the SDK code a little bit and when I add a console.log(request.name) into the add function in the client then it's giving me what I'd expect (BradyDowling). So Perhaps this issue is coming from a mismatch between what the client and the server are expecting here.

bradydowling commented 5 months ago

Hmmm, I tried using Axios instead and it worked great so it looks like this is an issue with the client. Here's my code in case that helps:

import axios from "axios";
import { createRequire } from "module";
import arg from "arg";
import fs from "fs";
import FormData from "form-data";

const customRequire = createRequire(import.meta.url);
const dotenv = customRequire("dotenv");
dotenv.config();

// Define the expected command-line arguments
const args = arg({
  // Types
  "--name": String,
  "--file": String,

  // Aliases
  "-n": "--name",
  "-f": "--file",
});

const name = args["--name"];
const filePath = args["--file"];

const run = async () => {
  if (!name || !filePath) {
    console.error("Please provide a voice name and file path");
    return;
  }
  const fullFilePath = `./input/audio-samples/${filePath}`;

  try {
    const form = new FormData();
    form.append("name", name);
    form.append("files", fs.createReadStream(fullFilePath));
    form.append("description", "Meditation voice created by Brady");

    const response = await axios.post(
      "https://api.elevenlabs.io/v1/voices/add",
      form,
      {
        headers: {
          "Content-Type": `multipart/form-data; boundary=${form._boundary}`,
          "xi-api-key": process.env.ELEVENLABS_API_KEY,
        },
      }
    );

    console.log(response.data);
  } catch (error) {
    console.error(
      "Error: ",
      error.response ? error.response.data : error.message
    );
  }
};

run();
plungepool commented 5 months ago

+1, experiencing the same thing.

dsinghvi commented 4 months ago

@plungepool @bradydowling this should be fixed in the latest SDK 0.8.2, let us know if you have any futher issues!