Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions src/catalog/catalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,19 @@ const MARKS_DIR = resolve(import.meta.dir, '../../public/marks');

describe('registry: ids, slugs, refs', () => {
it('pins the collection counts (bump intentionally when adding entries)', () => {
// 12 providers / 24 models: the 11 vendors + 23 TTS models, plus the Human
// 12 providers / 25 models: the 11 vendors + 24 TTS models, plus the Human
// baseline (provider `human`, model `human`). Two ElevenLabs entries are
// retired (2026-07-23), so the arena carries 22 of the 24.
// retired (2026-07-23) and Sonic 3.6 is unlisted pending its clips, so the
// arena carries 22 of the 25.
expect(BASE_PROVIDER_ENTRIES.length).toBe(12);
expect(BASE_MODEL_ENTRIES.length).toBe(24);
expect(BASE_MODEL_ENTRIES.length).toBe(25);
expect(arenaProviderEntries().length).toBe(12);
expect(arenaModelEntries().length).toBe(22);
// The committed registry carries no unlisted entries today; embargoed
// ones live in the private overlay until their providers announce them.
expect(BASE_MODEL_ENTRIES.filter((m) => m.status === 'unlisted')).toEqual(
[],
);
// Sonic 3.6 is committed but unlisted until its clips are generated;
// flipping status to 'active' is the one-line re-list.
expect(
BASE_MODEL_ENTRIES.filter((m) => m.status === 'unlisted').map((m) => m.id),
).toEqual(['cartesia-sonic-36']);
});

it('constrains the overlay to unlisted models with resolvable providers', () => {
Expand Down Expand Up @@ -167,6 +168,7 @@ describe('registry: ids, slugs, refs', () => {
'neuphonic-neu-hq',
'speechify-simba-3-2',
'fish-s21-pro',
'cartesia-sonic-36',
]);
const seedIds = SEED_STANDINGS.models.map((row) => row.id);
expect(new Set(seedIds).size).toBe(seedIds.length);
Expand Down Expand Up @@ -363,7 +365,7 @@ const EXPECTED_MODELS = [
},
{
id: 'cartesia-sonic-35',
arenaId: 'cartesia:sonic-3.5',
arenaId: 'cartesia:sonic-3.5-2026-05-04',
providerId: 'cartesia',
name: 'Sonic 3.5',
},
Expand Down Expand Up @@ -545,9 +547,9 @@ describe('server/catalog derivation equality', () => {
'2f77e73d0fe4bcfe4769ed7f4f44329c',
],
[
'variant:voice-nelliot:cartesia:sonic-3.5',
'variant:voice-nelliot:cartesia:sonic-3.5-2026-05-04',
'clip-20',
'7d3e067846de9940a7f4e3e0bde1cf68',
'76f803cefea4c90953672d0c763d8fc2',
],
];
for (const [variantId, promptId, hash] of goldens) {
Expand Down
68 changes: 62 additions & 6 deletions src/catalog/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ export const MODEL_ENTRIES: ModelEntry[] = [
{
question: "Is Sonic still Cartesia's current model?",
answer:
'No. Three newer generations have superseded it, with Sonic 3.5 as the current flagship. Sonic remains in the arena as the baseline of the family.',
'No. Four newer generations have superseded it, with Sonic 3.6 as the current flagship. Sonic remains in the arena as the baseline of the family.',
},
],
},
Expand Down Expand Up @@ -665,8 +665,8 @@ export const MODEL_ENTRIES: ModelEntry[] = [
slug: 'cartesia-sonic-3-5',
providerId: 'cartesia',
name: 'Sonic 3.5',
apiModelId: 'sonic-3.5',
arenaApiId: 'sonic-3.5',
apiModelId: 'sonic-3.5-2026-05-04',
arenaApiId: 'sonic-3.5-2026-05-04',
status: 'active',
releaseDate: {
value: '2026-05-04',
Expand All @@ -684,16 +684,16 @@ export const MODEL_ENTRIES: ModelEntry[] = [
fallbackClip: clip(
'voice-emma',
'cartesia',
'sonic-3.5',
'sonic-3.5-2026-05-04',
'clip-20',
'5e21eefdad513597d684cb9ec3b0815d',
'3e36a1883267dd73b9e11f42b78ede03',
),
},
copy: [
{
heading: 'Background',
paragraphs: [
"Sonic 3.5 is Cartesia's current flagship, released in May 2026. Cartesia positions it as its most natural and fastest model, with sub 90 ms latency and native support for 42 languages. It is tuned for production agent transcripts: it reads order numbers, emails, and confirmation codes correctly without preprocessing, and it resolves heteronyms like read and bow from the surrounding words.",
"Sonic 3.5 is the May 2026 Sonic snapshot, and was Cartesia's flagship until Sonic 3.6 shipped in August 2026. Cartesia positioned it as its most natural and fastest model at release, with sub 90 ms latency and native support for 42 languages. It is tuned for production agent transcripts: it reads order numbers, emails, and confirmation codes correctly without preprocessing, and it resolves heteronyms like read and bow from the surrounding words.",
],
sourceUrls: [
'https://docs.cartesia.ai/build-with-cartesia/tts-models/latest',
Expand All @@ -719,6 +719,62 @@ export const MODEL_ENTRIES: ModelEntry[] = [
],
},

{
id: 'cartesia-sonic-36',
slug: 'cartesia-sonic-3-6',
providerId: 'cartesia',
name: 'Sonic 3.6',
apiModelId: 'sonic-3.6-2026-08-27',
arenaApiId: 'sonic-3.6-2026-08-27',
status: 'unlisted',
releaseDate: {
value: '2026-08-27',
sourceUrl:
'https://docs.cartesia.ai/build-with-cartesia/tts-models/latest',
asOf: '2026-08-26',
note: 'Snapshot release.',
},
stats: {
// Measured only: needs a bench run against sonic-3.6-2026-08-27.
latencyMs: null,
languages: {
value: 44,
sourceUrl:
'https://docs.cartesia.ai/build-with-cartesia/tts-models/preview',
asOf: '2026-08-26',
},
},
voiceProfile: 27,
copy: [
{
heading: 'Background',
paragraphs: [
"Sonic 3.6 is Cartesia's current flagship model. Cartesia positions it as another step change in naturalness with sub 90 ms latency and native support for 44 languages. The model adapts intonation, pacing, and emotiveness to the conversation like a human would.",
],
sourceUrls: [
'https://docs.cartesia.ai/build-with-cartesia/tts-models/latest',
],
},
{
heading: 'At a glance',
paragraphs: [
'Alphanumerics and heteronyms without preprocessing, 44 languages, and a published sub 90 ms latency claim. It has not been through our 50 trial streaming benchmark yet.',
],
sourceUrls: [
'https://docs.cartesia.ai/build-with-cartesia/tts-models/latest',
],
},
],
faq: [
HOW_TESTED('Sonic 3.6'),
{
question: 'How fast is Sonic 3.6?',
answer:
'Cartesia publishes sub 90 ms latency. We have not yet measured it in our 50 trial streaming benchmark.',
},
],
},

/* --------------------------------- xAI ---------------------------------- */
{
id: 'xai-xai-tts',
Expand Down
40 changes: 21 additions & 19 deletions src/pipeline/transports/cartesia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,21 @@ import { BENCH_TEXT, postFormForJson, postJsonForBytes, wsTtfbTrial } from './ht
import type { ProviderTransport, TtfbPlan } from './types';

const API = 'https://api.cartesia.ai';
const VERSION = '2024-11-13';
const WS_VERSION = '2025-04-16';
/** The arena Clara clone — the voice the original 50-trial bench used. */

/** Cartesia's current published API version, sent on every call. */
const VERSION = '2026-08-14';

/** The arena Clara clone, the voice the original 50-trial bench used. */
const BENCH_VOICE = 'a5d537b0-4a5f-464d-ac12-d143fe1a0a36';

const apiKey = (): string => requireEnv('CARTESIA_API_KEY');
/** The four licensed source voices are English (see pipeline/voices.ts). */
const VOICE_LANGUAGE = 'en';

/** Bearer is the documented scheme; x-api-key is the pre-2026 form. */
const headers = (): Record<string, string> => ({
Authorization: `Bearer ${requireEnv('CARTESIA_API_KEY')}`,
'Cartesia-Version': VERSION,
});

export const cartesia: ProviderTransport = {
providerId: 'cartesia',
Expand All @@ -27,15 +36,13 @@ export const cartesia: ProviderTransport = {
synthesize: async ({ vendorModelId, providerVoiceId, text }) => ({
bytes: await postJsonForBytes(
`${API}/tts/bytes`,
{
'x-api-key': apiKey(),
'cartesia-version': VERSION,
accept: 'audio/mpeg',
},
{ ...headers(), accept: 'audio/mpeg' },
{
model_id: vendorModelId,
transcript: text,
voice: { mode: 'id', id: providerVoiceId },
// Cartesia recommends setting the language explicitly where possible.
language: VOICE_LANGUAGE,
output_format: { container: 'mp3', sample_rate: 44100, bit_rate: 128000 },
},
'cartesia',
Expand All @@ -51,11 +58,11 @@ export const cartesia: ProviderTransport = {
basename(sampleFiles[0]),
);
form.append('name', displayName);
form.append('language', 'en');
form.append('language', VOICE_LANGUAGE);
form.append('mode', 'similarity');
const result = await postFormForJson<{ id: string }>(
`${API}/voices/clone`,
{ 'x-api-key': apiKey(), 'cartesia-version': VERSION },
headers(),
form,
'cartesia voices/clone',
);
Expand All @@ -67,10 +74,7 @@ export const cartesia: ProviderTransport = {
trial: () =>
wsTtfbTrial({
url: 'wss://api.cartesia.ai/tts/websocket',
headers: {
Authorization: `Bearer ${apiKey()}`,
'Cartesia-Version': WS_VERSION,
},
headers: headers(),
framesFor: () => [
JSON.stringify({
model_id: vendorModelId,
Expand All @@ -83,14 +87,12 @@ export const cartesia: ProviderTransport = {
},
context_id: `bench-${Date.now()}`,
continue: false,
language: 'en',
language: VOICE_LANGUAGE,
}),
],
jsonHasAudio: (payload) => Boolean(payload.data),
jsonError: (payload) =>
payload.type === 'error' || payload.error
? JSON.stringify(payload)
: null,
payload.type === 'error' || payload.error ? JSON.stringify(payload) : null,
}),
}),
};