From 8ba6f8a829f740437989155b66b7460981c2d6e4 Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Fri, 15 Nov 2024 08:23:54 -0800 Subject: [PATCH 01/12] New clone method --- package.json | 2 +- src/types/index.ts | 34 +++++++++++++----------------- src/voices/index.ts | 50 ++++++++++++--------------------------------- 3 files changed, 28 insertions(+), 58 deletions(-) diff --git a/package.json b/package.json index 455d1e9..e11d70d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "name": "Cartesia", "url": "https://cartesia.ai" }, - "version": "1.2.2-alpha.0", + "version": "2.0.0-alpha.0", "description": "Client for the Cartesia API.", "type": "module", "module": "./dist/index.js", diff --git a/src/types/index.ts b/src/types/index.ts index f180a49..9db4531 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -119,27 +119,25 @@ export type EmitteryCallbacks = { events: Emittery["events"]; }; -export type CloneOptions = - | { - mode: "url"; - link: string; - enhance?: boolean; - } - | { - mode: "clip"; - clip: Blob; - enhance?: boolean; - }; - -export type HifiCloneOptions = CloneOptions & { +export type BaseCloneOptions = { + clip: Blob; + enhance?: boolean; name: string; description: string; language: Language; - model_id: string; - transcript?: string; - base_voice_id?: string; }; +export type CloneOptions = + | (BaseCloneOptions & { + mode: "stability"; + }) + | (BaseCloneOptions & { + mode: "similarity"; + model_id: string; + transcript?: string; + base_voice_id?: string; + }); + export type VoiceChangerOptions = { clip: File; voice: { id: string }; // match VoiceSpecifier shape, but only id is supported for now @@ -199,10 +197,6 @@ export type UpdateVoice = Partial< Pick >; -export type CloneResponse = { - embedding: number[]; -}; - export type VoiceChangerBytesResponse = { buffer: ArrayBuffer; }; diff --git a/src/voices/index.ts b/src/voices/index.ts index b6c5347..2d8d2f3 100644 --- a/src/voices/index.ts +++ b/src/voices/index.ts @@ -1,9 +1,7 @@ import { Client } from "../lib/client"; import type { CloneOptions, - CloneResponse, CreateVoice, - HifiCloneOptions, LocalizeOptions, LocalizeResponse, MixVoicesOptions, @@ -39,46 +37,25 @@ export default class Voices extends Client { return response.json() as Promise; } - async clone(options: CloneOptions): Promise { - if (options.mode === "clip") { - const formData = new FormData(); - formData.append("clip", options.clip); - if (options.enhance !== undefined) { - formData.append("enhance", options.enhance.toString()); - } - - const response = await this._fetch("/voices/clone/clip", { - method: "POST", - body: formData, - }); - return response.json(); - } - - throw new Error("Invalid mode for clone()"); - } - - async hifiClone(options: HifiCloneOptions): Promise { - if (options.mode !== "clip") { - throw new Error("Invalid mode for hifiClone()"); - } - + async clone(options: CloneOptions): Promise { const formData = new FormData(); formData.append("clip", options.clip); formData.append("name", options.name); formData.append("description", options.description); formData.append("language", options.language); - formData.append("model_id", options.model_id); if (options.enhance !== undefined) { formData.append("enhance", options.enhance.toString()); } - if (options.transcript) { - formData.append("transcript", options.transcript); - } - if (options.base_voice_id) { - formData.append("base_voice_id", options.base_voice_id); + if (options.mode === "similarity") { + if (options.transcript) { + formData.append("transcript", options.transcript); + } + if (options.base_voice_id) { + formData.append("base_voice_id", options.base_voice_id); + } } - - const response = await this._fetch("/voices/clone/hifi", { + + const response = await this._fetch("/voices/clone", { method: "POST", body: formData, }); @@ -86,14 +63,13 @@ export default class Voices extends Client { if (!response.ok) { if (response.headers.get("content-type")?.includes("application/json")) { const errorData = await response.json(); - throw new Error(errorData.message || "HiFi clone failed"); + throw new Error(errorData.message || "Clone voice failed"); } const errorText = await response.text(); - throw new Error(errorText || "HiFi clone failed"); + throw new Error(errorText || "Clone voice failed"); } - const data = await response.json(); - return data as Voice; + return response.json() as Promise; } async mix(options: MixVoicesOptions): Promise { From be4777f1a4c3431ad4154dc5884575b85897ab1c Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Mon, 18 Nov 2024 09:22:37 -0800 Subject: [PATCH 02/12] preserve backward-compatability --- src/types/index.ts | 18 +++++++++++++++++- src/voices/index.ts | 22 +++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index 9db4531..413d44f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -119,6 +119,22 @@ export type EmitteryCallbacks = { events: Emittery["events"]; }; +// deprecated +export type CloneOptions = + | { + mode: "url"; + link: string; + enhance?: boolean; + } + | { + mode: "clip"; + clip: Blob; + enhance?: boolean; + }; + +export type CloneResponse = { + embedding: number[]; +}; export type BaseCloneOptions = { clip: Blob; enhance?: boolean; @@ -127,7 +143,7 @@ export type BaseCloneOptions = { language: Language; }; -export type CloneOptions = +export type CreateCloneOptions = | (BaseCloneOptions & { mode: "stability"; }) diff --git a/src/voices/index.ts b/src/voices/index.ts index 2d8d2f3..e25c0e7 100644 --- a/src/voices/index.ts +++ b/src/voices/index.ts @@ -1,6 +1,8 @@ import { Client } from "../lib/client"; import type { CloneOptions, + CloneResponse, + CreateCloneOptions, CreateVoice, LocalizeOptions, LocalizeResponse, @@ -37,7 +39,25 @@ export default class Voices extends Client { return response.json() as Promise; } - async clone(options: CloneOptions): Promise { + async clone(options: CloneOptions): Promise { + if (options.mode === "clip") { + const formData = new FormData(); + formData.append("clip", options.clip); + if (options.enhance !== undefined) { + formData.append("enhance", options.enhance.toString()); + } + + const response = await this._fetch("/voices/clone/clip", { + method: "POST", + body: formData, + }); + return response.json(); + } + + throw new Error("Invalid mode for clone()"); + } + + async createClone(options: CreateCloneOptions): Promise { const formData = new FormData(); formData.append("clip", options.clip); formData.append("name", options.name); From bc4202d8d7bf20d7f4d2ba16f65d4db63c15e3dc Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Mon, 18 Nov 2024 09:23:40 -0800 Subject: [PATCH 03/12] version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e11d70d..49e478b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "name": "Cartesia", "url": "https://cartesia.ai" }, - "version": "2.0.0-alpha.0", + "version": "1.3.0-alpha.0", "description": "Client for the Cartesia API.", "type": "module", "module": "./dist/index.js", From 64c7838412c2300a6ebf998c7976802546af34d4 Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Mon, 18 Nov 2024 09:28:55 -0800 Subject: [PATCH 04/12] whitespace --- src/voices/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/voices/index.ts b/src/voices/index.ts index e25c0e7..a1e62b2 100644 --- a/src/voices/index.ts +++ b/src/voices/index.ts @@ -74,7 +74,7 @@ export default class Voices extends Client { formData.append("base_voice_id", options.base_voice_id); } } - + const response = await this._fetch("/voices/clone", { method: "POST", body: formData, From 00a007ba2e1747271fe1bd2878968560f3601e29 Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Tue, 19 Nov 2024 10:48:53 -0800 Subject: [PATCH 05/12] better backward compatibility --- src/types/index.ts | 47 ++++++++++++++++++++------------------------- src/voices/index.ts | 10 ++++------ 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index 413d44f..74a589d 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -119,40 +119,35 @@ export type EmitteryCallbacks = { events: Emittery["events"]; }; -// deprecated export type CloneOptions = - | { - mode: "url"; - link: string; - enhance?: boolean; - } - | { + | { // deprecated: mode: "clip"; clip: Blob; enhance?: boolean; - }; + } + | { + mode: "stability"; + clip: Blob; + enhance?: boolean; + name: string; + description: string; + language: Language; + } + | { + mode: "similarity"; + clip: Blob; + enhance?: boolean; + name: string; + description: string; + language: Language; + model_id: string; + transcript?: string; + base_voice_id?: string; + }; export type CloneResponse = { embedding: number[]; }; -export type BaseCloneOptions = { - clip: Blob; - enhance?: boolean; - name: string; - description: string; - language: Language; -}; - -export type CreateCloneOptions = - | (BaseCloneOptions & { - mode: "stability"; - }) - | (BaseCloneOptions & { - mode: "similarity"; - model_id: string; - transcript?: string; - base_voice_id?: string; - }); export type VoiceChangerOptions = { clip: File; diff --git a/src/voices/index.ts b/src/voices/index.ts index a1e62b2..102ab42 100644 --- a/src/voices/index.ts +++ b/src/voices/index.ts @@ -2,7 +2,6 @@ import { Client } from "../lib/client"; import type { CloneOptions, CloneResponse, - CreateCloneOptions, CreateVoice, LocalizeOptions, LocalizeResponse, @@ -39,7 +38,8 @@ export default class Voices extends Client { return response.json() as Promise; } - async clone(options: CloneOptions): Promise { + async clone(options: CloneOptions): Promise { + // First: handle old clip mode/endpoint if (options.mode === "clip") { const formData = new FormData(); formData.append("clip", options.clip); @@ -54,12 +54,9 @@ export default class Voices extends Client { return response.json(); } - throw new Error("Invalid mode for clone()"); - } - - async createClone(options: CreateCloneOptions): Promise { const formData = new FormData(); formData.append("clip", options.clip); + formData.append("mode", options.mode); formData.append("name", options.name); formData.append("description", options.description); formData.append("language", options.language); @@ -92,6 +89,7 @@ export default class Voices extends Client { return response.json() as Promise; } + async mix(options: MixVoicesOptions): Promise { const response = await this._fetch("/voices/mix", { method: "POST", From a9a1ee5520a6b4e851956684db945ace0858ee5c Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Tue, 19 Nov 2024 11:34:42 -0800 Subject: [PATCH 06/12] even better typing for backwards compatibility --- src/types/index.ts | 13 ++++++++++--- src/voices/index.ts | 5 ++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index 74a589d..9b8cc8b 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -120,12 +120,19 @@ export type EmitteryCallbacks = { }; export type CloneOptions = - | { // deprecated: - mode: "clip"; - clip: Blob; + | { + mode: "url"; + link: string; enhance?: boolean; } | { + mode: "clip"; + clip: Blob; + enhance?: boolean; + }; + +export type CloneVoiceOptions = + | { mode: "stability"; clip: Blob; enhance?: boolean; diff --git a/src/voices/index.ts b/src/voices/index.ts index 102ab42..f02e957 100644 --- a/src/voices/index.ts +++ b/src/voices/index.ts @@ -2,6 +2,7 @@ import { Client } from "../lib/client"; import type { CloneOptions, CloneResponse, + CloneVoiceOptions, CreateVoice, LocalizeOptions, LocalizeResponse, @@ -38,7 +39,9 @@ export default class Voices extends Client { return response.json() as Promise; } - async clone(options: CloneOptions): Promise { + async clone(options: CloneOptions): Promise + async clone(options: CloneVoiceOptions): Promise + async clone(options: CloneOptions | CloneVoiceOptions): Promise { // First: handle old clip mode/endpoint if (options.mode === "clip") { const formData = new FormData(); From 21d07620e0792256db43c885bd1bb7263316c619 Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Tue, 19 Nov 2024 11:36:19 -0800 Subject: [PATCH 07/12] fix whitespace --- src/types/index.ts | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index 9b8cc8b..a6122e7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -132,25 +132,25 @@ export type CloneOptions = }; export type CloneVoiceOptions = - | { - mode: "stability"; - clip: Blob; - enhance?: boolean; - name: string; - description: string; - language: Language; - } | { - mode: "similarity"; - clip: Blob; - enhance?: boolean; - name: string; - description: string; - language: Language; - model_id: string; - transcript?: string; - base_voice_id?: string; - }; + mode: "stability"; + clip: Blob; + enhance?: boolean; + name: string; + description: string; + language: Language; + } + | { + mode: "similarity"; + clip: Blob; + enhance?: boolean; + name: string; + description: string; + language: Language; + model_id: string; + transcript?: string; + base_voice_id?: string; + }; export type CloneResponse = { embedding: number[]; From bcd88f15d9cb31ccf79e8cecdcf81a52fddc677f Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Tue, 19 Nov 2024 11:51:38 -0800 Subject: [PATCH 08/12] remove mode: url --- src/types/index.ts | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index a6122e7..a89f350 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -119,17 +119,11 @@ export type EmitteryCallbacks = { events: Emittery["events"]; }; -export type CloneOptions = - | { - mode: "url"; - link: string; - enhance?: boolean; - } - | { - mode: "clip"; - clip: Blob; - enhance?: boolean; - }; +export type CloneOptions ={ + mode: "clip"; + clip: Blob; + enhance?: boolean; +}; export type CloneVoiceOptions = | { @@ -149,8 +143,8 @@ export type CloneVoiceOptions = language: Language; model_id: string; transcript?: string; - base_voice_id?: string; - }; + base_voice_id?: string; + }; export type CloneResponse = { embedding: number[]; From 6339d7ba8193ec9b2713995231dbfb740c9ba579 Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Tue, 19 Nov 2024 11:54:26 -0800 Subject: [PATCH 09/12] whitespace --- src/types/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index a89f350..c436c39 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -119,7 +119,7 @@ export type EmitteryCallbacks = { events: Emittery["events"]; }; -export type CloneOptions ={ +export type CloneOptions = { mode: "clip"; clip: Blob; enhance?: boolean; @@ -143,8 +143,8 @@ export type CloneVoiceOptions = language: Language; model_id: string; transcript?: string; - base_voice_id?: string; - }; + base_voice_id?: string; + }; export type CloneResponse = { embedding: number[]; From 4ad149206a233f08df7923d6d13c490470c4ecf0 Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Tue, 19 Nov 2024 11:56:14 -0800 Subject: [PATCH 10/12] remove unused options --- src/types/index.ts | 2 -- src/voices/index.ts | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index c436c39..ef6bdb6 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -141,9 +141,7 @@ export type CloneVoiceOptions = name: string; description: string; language: Language; - model_id: string; transcript?: string; - base_voice_id?: string; }; export type CloneResponse = { diff --git a/src/voices/index.ts b/src/voices/index.ts index f02e957..256ac18 100644 --- a/src/voices/index.ts +++ b/src/voices/index.ts @@ -70,9 +70,6 @@ export default class Voices extends Client { if (options.transcript) { formData.append("transcript", options.transcript); } - if (options.base_voice_id) { - formData.append("base_voice_id", options.base_voice_id); - } } const response = await this._fetch("/voices/clone", { From 698eccf50f4bd1db12a2405277823be850e52bef Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Fri, 29 Nov 2024 23:20:01 +0900 Subject: [PATCH 11/12] whitespace --- src/voices/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/voices/index.ts b/src/voices/index.ts index 256ac18..07ea0ae 100644 --- a/src/voices/index.ts +++ b/src/voices/index.ts @@ -89,7 +89,6 @@ export default class Voices extends Client { return response.json() as Promise; } - async mix(options: MixVoicesOptions): Promise { const response = await this._fetch("/voices/mix", { method: "POST", From 7c5fac5080bdc518db7e4f23c6c2dd2a36c8794a Mon Sep 17 00:00:00 2001 From: Noah Tye Date: Wed, 11 Dec 2024 09:28:30 -0800 Subject: [PATCH 12/12] version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b04822d..14f4952 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "name": "Cartesia", "url": "https://cartesia.ai" }, - "version": "1.4.0-alpha.0", + "version": "1.4.0", "description": "Client for the Cartesia API.", "type": "module", "module": "./dist/index.js",