From e68beb241e1a794ef65579492f12dff947b1c814 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 20 May 2026 13:14:24 -0700 Subject: [PATCH 1/2] improvement(elevenlabs): wire stability and similarity_boost end-to-end --- apps/sim/app/api/tools/tts/route.ts | 18 +++++++++++-- apps/sim/blocks/blocks/elevenlabs.ts | 27 ++++++++++++++++++- apps/sim/lib/api/contracts/tools/media/tts.ts | 2 ++ apps/sim/tools/elevenlabs/tts.ts | 8 +++--- apps/sim/tools/elevenlabs/types.ts | 9 ++----- 5 files changed, 50 insertions(+), 14 deletions(-) diff --git a/apps/sim/app/api/tools/tts/route.ts b/apps/sim/app/api/tools/tts/route.ts index 7a82e57eebf..e0b7727c0be 100644 --- a/apps/sim/app/api/tools/tts/route.ts +++ b/apps/sim/app/api/tools/tts/route.ts @@ -35,8 +35,17 @@ export const POST = withRouteHandler(async (request: NextRequest) => { ) if (!parsed.success) return parsed.response - const { text, voiceId, apiKey, modelId, workspaceId, workflowId, executionId } = - parsed.data.body + const { + text, + voiceId, + apiKey, + modelId, + stability, + similarityBoost, + workspaceId, + workflowId, + executionId, + } = parsed.data.body const voiceIdValidation = validateAlphanumericId(voiceId, 'voiceId', 255) if (!voiceIdValidation.isValid) { @@ -57,6 +66,10 @@ export const POST = withRouteHandler(async (request: NextRequest) => { const endpoint = `https://api.elevenlabs.io/v1/text-to-speech/${voiceId}` + const voiceSettings: { stability?: number; similarity_boost?: number } = {} + if (stability !== undefined) voiceSettings.stability = stability + if (similarityBoost !== undefined) voiceSettings.similarity_boost = similarityBoost + const response = await fetch(endpoint, { method: 'POST', headers: { @@ -67,6 +80,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { body: JSON.stringify({ text, model_id: modelId, + ...(Object.keys(voiceSettings).length > 0 ? { voice_settings: voiceSettings } : {}), }), signal: AbortSignal.timeout(DEFAULT_EXECUTION_TIMEOUT_MS), }) diff --git a/apps/sim/blocks/blocks/elevenlabs.ts b/apps/sim/blocks/blocks/elevenlabs.ts index b514fa556f8..4682a9ce6c2 100644 --- a/apps/sim/blocks/blocks/elevenlabs.ts +++ b/apps/sim/blocks/blocks/elevenlabs.ts @@ -5,7 +5,7 @@ import type { ElevenLabsBlockResponse } from '@/tools/elevenlabs/types' export const ElevenLabsBlock: BlockConfig = { type: 'elevenlabs', name: 'ElevenLabs', - description: 'Convert TTS using ElevenLabs', + description: 'Convert text to speech with ElevenLabs', authMode: AuthMode.ApiKey, longDescription: 'Integrate ElevenLabs into the workflow. Can convert text to speech.', docsLink: 'https://docs.sim.ai/tools/elevenlabs', @@ -42,6 +42,21 @@ export const ElevenLabsBlock: BlockConfig = { { label: 'eleven_flash_v2_5', id: 'eleven_flash_v2_5' }, { label: 'eleven_v3', id: 'eleven_v3' }, ], + value: () => 'eleven_monolingual_v1', + }, + { + id: 'stability', + title: 'Stability', + type: 'short-input', + placeholder: '0.0 to 1.0 (e.g., 0.5)', + mode: 'advanced', + }, + { + id: 'similarityBoost', + title: 'Similarity Boost', + type: 'short-input', + placeholder: '0.0 to 1.0 (e.g., 0.75)', + mode: 'advanced', }, { id: 'apiKey', @@ -62,6 +77,14 @@ export const ElevenLabsBlock: BlockConfig = { text: params.text, voiceId: params.voiceId, modelId: params.modelId, + stability: + params.stability !== undefined && params.stability !== '' + ? Number(params.stability) + : undefined, + similarityBoost: + params.similarityBoost !== undefined && params.similarityBoost !== '' + ? Number(params.similarityBoost) + : undefined, }), }, }, @@ -70,6 +93,8 @@ export const ElevenLabsBlock: BlockConfig = { text: { type: 'string', description: 'Text to convert' }, voiceId: { type: 'string', description: 'Voice identifier' }, modelId: { type: 'string', description: 'Model identifier' }, + stability: { type: 'number', description: 'Voice stability 0.0-1.0' }, + similarityBoost: { type: 'number', description: 'Similarity boost 0.0-1.0' }, apiKey: { type: 'string', description: 'ElevenLabs API key' }, }, diff --git a/apps/sim/lib/api/contracts/tools/media/tts.ts b/apps/sim/lib/api/contracts/tools/media/tts.ts index 3a5b68e50a8..fdff389ccde 100644 --- a/apps/sim/lib/api/contracts/tools/media/tts.ts +++ b/apps/sim/lib/api/contracts/tools/media/tts.ts @@ -7,6 +7,8 @@ export const ttsToolBodySchema = z.object({ voiceId: z.string({ error: 'Missing required parameters' }).min(1, 'Missing required parameters'), apiKey: z.string({ error: 'Missing required parameters' }).min(1, 'Missing required parameters'), modelId: z.string().optional().default('eleven_monolingual_v1'), + stability: z.coerce.number().min(0).max(1).optional(), + similarityBoost: z.coerce.number().min(0).max(1).optional(), workspaceId: z.string().optional(), workflowId: z.string().optional(), executionId: z.string().optional(), diff --git a/apps/sim/tools/elevenlabs/tts.ts b/apps/sim/tools/elevenlabs/tts.ts index e0a42e010ca..2faee98a5e6 100644 --- a/apps/sim/tools/elevenlabs/tts.ts +++ b/apps/sim/tools/elevenlabs/tts.ts @@ -4,7 +4,7 @@ import type { ToolConfig } from '@/tools/types' export const elevenLabsTtsTool: ToolConfig = { id: 'elevenlabs_tts', name: 'ElevenLabs TTS', - description: 'Convert TTS using ElevenLabs voices', + description: 'Convert text to speech using ElevenLabs voices', version: '1.0.0', params: { @@ -34,7 +34,7 @@ export const elevenLabsTtsTool: ToolConfig ({ + headers: () => ({ 'Content-Type': 'application/json', }), body: ( @@ -65,7 +65,7 @@ export const elevenLabsTtsTool: ToolConfig Date: Wed, 20 May 2026 13:21:48 -0700 Subject: [PATCH 2/2] fix(elevenlabs): guard NaN in voice settings and always send both knobs together --- apps/sim/app/api/tools/tts/route.ts | 12 ++++++++---- apps/sim/blocks/blocks/elevenlabs.ts | 29 ++++++++++++++-------------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/apps/sim/app/api/tools/tts/route.ts b/apps/sim/app/api/tools/tts/route.ts index e0b7727c0be..929e995c1da 100644 --- a/apps/sim/app/api/tools/tts/route.ts +++ b/apps/sim/app/api/tools/tts/route.ts @@ -66,9 +66,13 @@ export const POST = withRouteHandler(async (request: NextRequest) => { const endpoint = `https://api.elevenlabs.io/v1/text-to-speech/${voiceId}` - const voiceSettings: { stability?: number; similarity_boost?: number } = {} - if (stability !== undefined) voiceSettings.stability = stability - if (similarityBoost !== undefined) voiceSettings.similarity_boost = similarityBoost + const hasVoiceSetting = stability !== undefined || similarityBoost !== undefined + const voiceSettings = hasVoiceSetting + ? { + stability: stability ?? 0.5, + similarity_boost: similarityBoost ?? 0.75, + } + : undefined const response = await fetch(endpoint, { method: 'POST', @@ -80,7 +84,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => { body: JSON.stringify({ text, model_id: modelId, - ...(Object.keys(voiceSettings).length > 0 ? { voice_settings: voiceSettings } : {}), + ...(voiceSettings ? { voice_settings: voiceSettings } : {}), }), signal: AbortSignal.timeout(DEFAULT_EXECUTION_TIMEOUT_MS), }) diff --git a/apps/sim/blocks/blocks/elevenlabs.ts b/apps/sim/blocks/blocks/elevenlabs.ts index 4682a9ce6c2..079ea4ee50e 100644 --- a/apps/sim/blocks/blocks/elevenlabs.ts +++ b/apps/sim/blocks/blocks/elevenlabs.ts @@ -72,20 +72,21 @@ export const ElevenLabsBlock: BlockConfig = { access: ['elevenlabs_tts'], config: { tool: () => 'elevenlabs_tts', - params: (params) => ({ - apiKey: params.apiKey, - text: params.text, - voiceId: params.voiceId, - modelId: params.modelId, - stability: - params.stability !== undefined && params.stability !== '' - ? Number(params.stability) - : undefined, - similarityBoost: - params.similarityBoost !== undefined && params.similarityBoost !== '' - ? Number(params.similarityBoost) - : undefined, - }), + params: (params) => { + const parseUnitInterval = (value: unknown): number | undefined => { + if (value === undefined || value === null || value === '') return undefined + const n = Number(value) + return Number.isFinite(n) ? n : undefined + } + return { + apiKey: params.apiKey, + text: params.text, + voiceId: params.voiceId, + modelId: params.modelId, + stability: parseUnitInterval(params.stability), + similarityBoost: parseUnitInterval(params.similarityBoost), + } + }, }, },