Files
video/tools/audio/elevenlabs_tts.py
calesthioandHanzo Dev 7ffb943132 Initial release — OpenMontage: the first open-source agentic video production system
11 production pipelines, 47 tools, 124 agent skills.
Supports cloud APIs (fal.ai, OpenAI, ElevenLabs, Suno, HeyGen, Runway) and
free local providers (diffusers, Piper TTS, WAN 2.1, Hunyuan, CogVideo).

Co-authored-by: Hanzo Dev <[email protected]>
2026-03-29 08:25:17 -07:00

188 lines
5.8 KiB
Python

"""ElevenLabs text-to-speech provider tool."""
from __future__ import annotations
import os
import time
from pathlib import Path
from typing import Any
from tools.base_tool import (
BaseTool,
Determinism,
ExecutionMode,
ResourceProfile,
RetryPolicy,
ToolResult,
ToolRuntime,
ToolStability,
ToolStatus,
ToolTier,
)
class ElevenLabsTTS(BaseTool):
name = "elevenlabs_tts"
version = "0.1.0"
tier = ToolTier.VOICE
capability = "tts"
provider = "elevenlabs"
stability = ToolStability.EXPERIMENTAL
execution_mode = ExecutionMode.SYNC
determinism = Determinism.STOCHASTIC
runtime = ToolRuntime.API
dependencies = []
install_instructions = (
"Set the ELEVENLABS_API_KEY environment variable:\n"
" export ELEVENLABS_API_KEY=your_key_here\n"
"Get a key at https://elevenlabs.io"
)
fallback = "openai_tts"
fallback_tools = ["openai_tts", "piper_tts"]
agent_skills = ["elevenlabs", "text-to-speech"]
capabilities = [
"text_to_speech",
"voice_selection",
"ssml_support",
"pronunciation_control",
]
supports = {
"voice_cloning": True,
"multilingual": True,
"offline": False,
"native_audio": True,
}
best_for = [
"high-quality narration",
"voice-sensitive spokesperson videos",
"multilingual spoken delivery",
]
not_good_for = [
"fully offline production",
"privacy-constrained local-only workflows",
]
input_schema = {
"type": "object",
"required": ["text"],
"properties": {
"text": {"type": "string", "description": "Text to convert to speech"},
"voice_id": {
"type": "string",
"description": "ElevenLabs voice ID (default: Rachel)",
},
"model_id": {
"type": "string",
"default": "eleven_multilingual_v2",
"description": "TTS model to use",
},
"stability": {
"type": "number",
"default": 0.5,
"minimum": 0,
"maximum": 1,
},
"similarity_boost": {
"type": "number",
"default": 0.75,
"minimum": 0,
"maximum": 1,
},
"style": {
"type": "number",
"default": 0.0,
"minimum": 0,
"maximum": 1,
},
"output_path": {"type": "string"},
"output_format": {
"type": "string",
"default": "mp3_44100_128",
"enum": ["mp3_44100_128", "mp3_44100_192", "pcm_16000", "pcm_24000"],
},
},
}
resource_profile = ResourceProfile(
cpu_cores=1, ram_mb=256, vram_mb=0, disk_mb=50, network_required=True
)
retry_policy = RetryPolicy(max_retries=2, retryable_errors=["rate_limit", "timeout"])
idempotency_key_fields = ["text", "voice_id", "model_id"]
side_effects = ["writes audio file to output_path", "calls ElevenLabs API"]
user_visible_verification = ["Listen to generated audio for natural speech quality"]
DEFAULT_VOICE_ID = "21m00Tcm4TlvDq8ikWAM"
def get_status(self) -> ToolStatus:
if os.environ.get("ELEVENLABS_API_KEY"):
return ToolStatus.AVAILABLE
return ToolStatus.UNAVAILABLE
def estimate_cost(self, inputs: dict[str, Any]) -> float:
return round(len(inputs.get("text", "")) * 0.0003, 4)
def execute(self, inputs: dict[str, Any]) -> ToolResult:
api_key = os.environ.get("ELEVENLABS_API_KEY")
if not api_key:
return ToolResult(success=False, error="No ElevenLabs API key. " + self.install_instructions)
start = time.time()
try:
result = self._generate(inputs, api_key)
except Exception as exc:
return ToolResult(success=False, error=f"TTS generation failed: {exc}")
result.duration_seconds = round(time.time() - start, 2)
result.cost_usd = self.estimate_cost(inputs)
return result
def _generate(self, inputs: dict[str, Any], api_key: str) -> ToolResult:
import requests
text = inputs["text"]
voice_id = inputs.get("voice_id", self.DEFAULT_VOICE_ID)
model_id = inputs.get("model_id", "eleven_multilingual_v2")
output_format = inputs.get("output_format", "mp3_44100_128")
response = requests.post(
f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}",
headers={
"xi-api-key": api_key,
"Content-Type": "application/json",
"Accept": "audio/mpeg",
},
json={
"text": text,
"model_id": model_id,
"voice_settings": {
"stability": inputs.get("stability", 0.5),
"similarity_boost": inputs.get("similarity_boost", 0.75),
"style": inputs.get("style", 0.0),
},
},
params={"output_format": output_format},
timeout=120,
)
response.raise_for_status()
ext = "mp3" if "mp3" in output_format else "wav"
output_path = Path(inputs.get("output_path", f"tts_output.{ext}"))
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_bytes(response.content)
return ToolResult(
success=True,
data={
"provider": self.provider,
"model": model_id,
"voice_id": voice_id,
"text_length": len(text),
"output": str(output_path),
"format": output_format,
},
artifacts=[str(output_path)],
model=model_id,
)