@agentforge-ai/tools-voice 0.7.1 → 0.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,123 +1 @@
1
- import * as _mastra_core_tools from '@mastra/core/tools';
2
-
3
- /**
4
- * Voice configuration types for AgentForge voice tools.
5
- */
6
- interface VoiceConfig {
7
- /** ElevenLabs voice ID */
8
- voiceId?: string;
9
- /** Speech speed multiplier (0.5–2.0). Default: 1.0 */
10
- speed?: number;
11
- /** TTS model ID (e.g., 'eleven_multilingual_v2'). Default: 'eleven_multilingual_v2' */
12
- model?: string;
13
- /** Voice provider. Default: 'elevenlabs' */
14
- provider?: 'elevenlabs';
15
- /** Language code (e.g., 'en', 'es'). Default: 'en' */
16
- language?: string;
17
- }
18
- declare const DEFAULT_VOICE_CONFIG: Required<VoiceConfig>;
19
- /** Maximum text length for TTS input (ElevenLabs limit) */
20
- declare const MAX_TTS_TEXT_LENGTH = 5000;
21
- /** Maximum audio file size for STT (OpenAI Whisper limit: 25MB) */
22
- declare const MAX_STT_FILE_SIZE: number;
23
- /**
24
- * Sanitize text input for TTS.
25
- * Strips control characters and trims whitespace.
26
- * Throws if text exceeds maximum length.
27
- */
28
- declare function sanitizeTtsText(text: string): string;
29
-
30
- /**
31
- * ElevenLabs TTS Client for AgentForge.
32
- *
33
- * Sends text to the ElevenLabs text-to-speech API and returns audio data.
34
- * API Reference: https://elevenlabs.io/docs/api-reference/text-to-speech
35
- */
36
-
37
- interface TtsRequest {
38
- /** Text to convert to speech */
39
- text: string;
40
- /** Voice configuration overrides */
41
- config?: Partial<VoiceConfig>;
42
- }
43
- interface TtsResponse {
44
- /** Audio data as ArrayBuffer */
45
- audio: ArrayBuffer;
46
- /** Content type of the audio (e.g., 'audio/mpeg') */
47
- contentType: string;
48
- /** Character count of the input text */
49
- characterCount: number;
50
- }
51
- /**
52
- * Convert text to speech using the ElevenLabs API.
53
- *
54
- * @throws Error if ELEVENLABS_API_KEY is not set
55
- * @throws Error if the API request fails
56
- * @throws Error if text validation fails
57
- */
58
- declare function textToSpeech(request: TtsRequest): Promise<TtsResponse>;
59
-
60
- /**
61
- * OpenAI Whisper STT Client for AgentForge.
62
- *
63
- * Sends audio data to the OpenAI Whisper transcription API.
64
- * API Reference: https://platform.openai.com/docs/guides/speech-to-text
65
- */
66
- interface SttRequest {
67
- /** Audio data as ArrayBuffer or Uint8Array (cross-platform; no Node.js Buffer) */
68
- audio: ArrayBuffer | Uint8Array;
69
- /** Audio file name with extension (used for MIME type detection). Default: 'audio.ogg' */
70
- fileName?: string;
71
- /** Language hint (ISO-639-1 code). Optional. */
72
- language?: string;
73
- /** Whisper model to use. Default: 'whisper-1' */
74
- model?: string;
75
- }
76
- interface SttResponse {
77
- /** Transcribed text */
78
- text: string;
79
- /** Language detected or used */
80
- language?: string;
81
- }
82
- /**
83
- * Transcribe audio to text using the OpenAI Whisper API.
84
- *
85
- * @throws Error if OPENAI_API_KEY is not set
86
- * @throws Error if the API request fails
87
- * @throws Error if audio exceeds size limit
88
- */
89
- declare function speechToText(request: SttRequest): Promise<SttResponse>;
90
-
91
- /**
92
- * Create a Mastra voice tool that provides TTS and STT capabilities.
93
- *
94
- * @param defaultConfig - Default voice configuration for TTS
95
- * @returns A Mastra tool with text-to-speech and speech-to-text actions
96
- *
97
- * @example
98
- * ```typescript
99
- * import { createVoiceTool } from '@agentforge-ai/tools-voice';
100
- *
101
- * const voiceTool = createVoiceTool({ voiceId: 'custom-voice-id' });
102
- * // Register with Mastra agent
103
- * ```
104
- */
105
- declare function createVoiceTool(defaultConfig?: Partial<VoiceConfig>): _mastra_core_tools.Tool<{
106
- action: "text-to-speech" | "speech-to-text";
107
- voiceId?: string | undefined;
108
- speed?: number | undefined;
109
- language?: string | undefined;
110
- text?: string | undefined;
111
- audioBase64?: string | undefined;
112
- fileName?: string | undefined;
113
- }, {
114
- action: string;
115
- success: boolean;
116
- contentType?: string | undefined;
117
- characterCount?: number | undefined;
118
- text?: string | undefined;
119
- audioBase64?: string | undefined;
120
- error?: string | undefined;
121
- }, unknown, unknown, _mastra_core_tools.ToolExecutionContext<unknown, unknown, unknown>, "agentforge-voice", unknown>;
122
-
123
- export { DEFAULT_VOICE_CONFIG, MAX_STT_FILE_SIZE, MAX_TTS_TEXT_LENGTH, type SttRequest, type SttResponse, type TtsRequest, type TtsResponse, type VoiceConfig, createVoiceTool, sanitizeTtsText, speechToText, textToSpeech };
1
+ export * from '@agentforge-ai/core';
package/dist/index.js CHANGED
@@ -1,266 +1,3 @@
1
- // src/voice-config.ts
2
- var DEFAULT_VOICE_CONFIG = {
3
- voiceId: "21m00Tcm4TlvDq8ikWAM",
4
- speed: 1,
5
- model: "eleven_multilingual_v2",
6
- provider: "elevenlabs",
7
- language: "en"
8
- };
9
- var MAX_TTS_TEXT_LENGTH = 5e3;
10
- var MAX_STT_FILE_SIZE = 25 * 1024 * 1024;
11
- function sanitizeTtsText(text) {
12
- if (!text || typeof text !== "string") {
13
- throw new Error("TTS text must be a non-empty string");
14
- }
15
- const sanitized = text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "").trim();
16
- if (sanitized.length === 0) {
17
- throw new Error("TTS text is empty after sanitization");
18
- }
19
- if (sanitized.length > MAX_TTS_TEXT_LENGTH) {
20
- throw new Error(
21
- `TTS text exceeds maximum length of ${MAX_TTS_TEXT_LENGTH} characters (got ${sanitized.length})`
22
- );
23
- }
24
- return sanitized;
25
- }
26
-
27
- // src/tts-client.ts
28
- var ELEVENLABS_API_BASE = "https://api.elevenlabs.io/v1";
29
- async function textToSpeech(request) {
30
- const apiKey = process.env.ELEVENLABS_API_KEY;
31
- if (!apiKey) {
32
- throw new Error(
33
- "ELEVENLABS_API_KEY environment variable is required for TTS. Get your key at https://elevenlabs.io"
34
- );
35
- }
36
- const sanitizedText = sanitizeTtsText(request.text);
37
- const config = {
38
- voiceId: request.config?.voiceId ?? DEFAULT_VOICE_CONFIG.voiceId,
39
- model: request.config?.model ?? DEFAULT_VOICE_CONFIG.model,
40
- speed: request.config?.speed ?? DEFAULT_VOICE_CONFIG.speed,
41
- language: request.config?.language ?? DEFAULT_VOICE_CONFIG.language
42
- };
43
- const url = `${ELEVENLABS_API_BASE}/text-to-speech/${config.voiceId}`;
44
- const body = {
45
- text: sanitizedText,
46
- model_id: config.model,
47
- voice_settings: {
48
- stability: 0.5,
49
- similarity_boost: 0.75,
50
- speed: config.speed
51
- },
52
- ...config.language !== "en" ? { language_code: config.language } : {}
53
- };
54
- const response = await fetch(url, {
55
- method: "POST",
56
- headers: {
57
- "Content-Type": "application/json",
58
- "xi-api-key": apiKey,
59
- "Accept": "audio/mpeg"
60
- },
61
- body: JSON.stringify(body)
62
- });
63
- if (!response.ok) {
64
- const errorText = await response.text().catch(() => "Unknown error");
65
- throw new Error(
66
- `ElevenLabs TTS API error (${response.status}): ${errorText}`
67
- );
68
- }
69
- const audio = await response.arrayBuffer();
70
- const contentType = response.headers.get("content-type") || "audio/mpeg";
71
- return {
72
- audio,
73
- contentType,
74
- characterCount: sanitizedText.length
75
- };
76
- }
77
-
78
- // src/stt-client.ts
79
- var OPENAI_API_BASE = "https://api.openai.com/v1";
80
- async function speechToText(request) {
81
- const apiKey = process.env.OPENAI_API_KEY;
82
- if (!apiKey) {
83
- throw new Error(
84
- "OPENAI_API_KEY environment variable is required for STT. Get your key at https://platform.openai.com"
85
- );
86
- }
87
- const audioBuffer = (() => {
88
- if (request.audio instanceof ArrayBuffer) return request.audio;
89
- const copy = new ArrayBuffer(request.audio.byteLength);
90
- new Uint8Array(copy).set(request.audio);
91
- return copy;
92
- })();
93
- if (audioBuffer.byteLength > MAX_STT_FILE_SIZE) {
94
- throw new Error(
95
- `Audio file exceeds maximum size of ${MAX_STT_FILE_SIZE / (1024 * 1024)}MB (got ${(audioBuffer.byteLength / (1024 * 1024)).toFixed(1)}MB)`
96
- );
97
- }
98
- if (audioBuffer.byteLength === 0) {
99
- throw new Error("Audio data is empty");
100
- }
101
- const fileName = request.fileName || "audio.ogg";
102
- const model = request.model || "whisper-1";
103
- const blob = new Blob([audioBuffer], { type: getMimeType(fileName) });
104
- const formData = new FormData();
105
- formData.append("file", blob, fileName);
106
- formData.append("model", model);
107
- if (request.language) {
108
- formData.append("language", request.language);
109
- }
110
- const response = await fetch(`${OPENAI_API_BASE}/audio/transcriptions`, {
111
- method: "POST",
112
- headers: {
113
- "Authorization": `Bearer ${apiKey}`
114
- },
115
- body: formData
116
- });
117
- if (!response.ok) {
118
- const errorText = await response.text().catch(() => "Unknown error");
119
- throw new Error(
120
- `OpenAI Whisper STT API error (${response.status}): ${errorText}`
121
- );
122
- }
123
- const result = await response.json();
124
- return {
125
- text: result.text,
126
- language: result.language ?? request.language
127
- };
128
- }
129
- function getMimeType(fileName) {
130
- const ext = fileName.split(".").pop()?.toLowerCase();
131
- const mimeMap = {
132
- mp3: "audio/mpeg",
133
- mp4: "audio/mp4",
134
- mpeg: "audio/mpeg",
135
- mpga: "audio/mpeg",
136
- m4a: "audio/mp4",
137
- wav: "audio/wav",
138
- webm: "audio/webm",
139
- ogg: "audio/ogg",
140
- oga: "audio/ogg",
141
- flac: "audio/flac"
142
- };
143
- return mimeMap[ext || ""] || "audio/ogg";
144
- }
145
-
146
- // src/voice-tool.ts
147
- import { createTool } from "@mastra/core/tools";
148
- import { z } from "zod";
149
- function createVoiceTool(defaultConfig) {
150
- return createTool({
151
- id: "agentforge-voice",
152
- description: "Voice capabilities: convert text to speech (TTS) using ElevenLabs, or transcribe speech to text (STT) using OpenAI Whisper.",
153
- inputSchema: z.object({
154
- action: z.enum(["text-to-speech", "speech-to-text"]).describe(
155
- "The voice action to perform"
156
- ),
157
- text: z.string().optional().describe(
158
- "Text to convert to speech (required for text-to-speech action)"
159
- ),
160
- audioBase64: z.string().optional().describe(
161
- "Base64-encoded audio data (required for speech-to-text action)"
162
- ),
163
- fileName: z.string().optional().describe(
164
- 'Audio file name with extension for STT (e.g., "audio.ogg")'
165
- ),
166
- language: z.string().optional().describe(
167
- 'Language code (e.g., "en", "es"). Used for both TTS language and STT hint.'
168
- ),
169
- voiceId: z.string().optional().describe(
170
- "ElevenLabs voice ID override"
171
- ),
172
- speed: z.number().min(0.5).max(2).optional().describe(
173
- "Speech speed multiplier (0.5\u20132.0)"
174
- )
175
- }),
176
- outputSchema: z.object({
177
- success: z.boolean(),
178
- action: z.string(),
179
- text: z.string().optional(),
180
- audioBase64: z.string().optional(),
181
- contentType: z.string().optional(),
182
- characterCount: z.number().optional(),
183
- error: z.string().optional()
184
- }),
185
- execute: async (inputData) => {
186
- try {
187
- if (inputData.action === "text-to-speech") {
188
- if (!inputData.text) {
189
- return {
190
- success: false,
191
- action: "text-to-speech",
192
- error: "Text is required for text-to-speech action"
193
- };
194
- }
195
- const result = await textToSpeech({
196
- text: inputData.text,
197
- config: {
198
- ...defaultConfig,
199
- ...inputData.voiceId ? { voiceId: inputData.voiceId } : {},
200
- ...inputData.speed ? { speed: inputData.speed } : {},
201
- ...inputData.language ? { language: inputData.language } : {}
202
- }
203
- });
204
- const bytes = new Uint8Array(result.audio);
205
- let binary = "";
206
- for (let i = 0; i < bytes.byteLength; i++) {
207
- binary += String.fromCharCode(bytes[i]);
208
- }
209
- const audioBase64 = btoa(binary);
210
- return {
211
- success: true,
212
- action: "text-to-speech",
213
- audioBase64,
214
- contentType: result.contentType,
215
- characterCount: result.characterCount
216
- };
217
- }
218
- if (inputData.action === "speech-to-text") {
219
- if (!inputData.audioBase64) {
220
- return {
221
- success: false,
222
- action: "speech-to-text",
223
- error: "audioBase64 is required for speech-to-text action"
224
- };
225
- }
226
- const binaryString = atob(inputData.audioBase64);
227
- const bytes = new Uint8Array(binaryString.length);
228
- for (let i = 0; i < binaryString.length; i++) {
229
- bytes[i] = binaryString.charCodeAt(i);
230
- }
231
- const result = await speechToText({
232
- audio: bytes,
233
- fileName: inputData.fileName,
234
- language: inputData.language
235
- });
236
- return {
237
- success: true,
238
- action: "speech-to-text",
239
- text: result.text
240
- };
241
- }
242
- return {
243
- success: false,
244
- action: inputData.action,
245
- error: `Unknown action: ${inputData.action}`
246
- };
247
- } catch (error) {
248
- return {
249
- success: false,
250
- action: inputData.action,
251
- error: error instanceof Error ? error.message : String(error)
252
- };
253
- }
254
- }
255
- });
256
- }
257
- export {
258
- DEFAULT_VOICE_CONFIG,
259
- MAX_STT_FILE_SIZE,
260
- MAX_TTS_TEXT_LENGTH,
261
- createVoiceTool,
262
- sanitizeTtsText,
263
- speechToText,
264
- textToSpeech
265
- };
1
+ // src/index.ts
2
+ export * from "@agentforge-ai/core";
266
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/voice-config.ts","../src/tts-client.ts","../src/stt-client.ts","../src/voice-tool.ts"],"sourcesContent":["/**\n * Voice configuration types for AgentForge voice tools.\n */\n\nexport interface VoiceConfig {\n /** ElevenLabs voice ID */\n voiceId?: string;\n /** Speech speed multiplier (0.5–2.0). Default: 1.0 */\n speed?: number;\n /** TTS model ID (e.g., 'eleven_multilingual_v2'). Default: 'eleven_multilingual_v2' */\n model?: string;\n /** Voice provider. Default: 'elevenlabs' */\n provider?: 'elevenlabs';\n /** Language code (e.g., 'en', 'es'). Default: 'en' */\n language?: string;\n}\n\nexport const DEFAULT_VOICE_CONFIG: Required<VoiceConfig> = {\n voiceId: '21m00Tcm4TlvDq8ikWAM',\n speed: 1.0,\n model: 'eleven_multilingual_v2',\n provider: 'elevenlabs',\n language: 'en',\n};\n\n/** Maximum text length for TTS input (ElevenLabs limit) */\nexport const MAX_TTS_TEXT_LENGTH = 5000;\n\n/** Maximum audio file size for STT (OpenAI Whisper limit: 25MB) */\nexport const MAX_STT_FILE_SIZE = 25 * 1024 * 1024;\n\n/**\n * Sanitize text input for TTS.\n * Strips control characters and trims whitespace.\n * Throws if text exceeds maximum length.\n */\nexport function sanitizeTtsText(text: string): string {\n if (!text || typeof text !== 'string') {\n throw new Error('TTS text must be a non-empty string');\n }\n\n // Strip control characters (except newline, tab)\n const sanitized = text.replace(/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/g, '').trim();\n\n if (sanitized.length === 0) {\n throw new Error('TTS text is empty after sanitization');\n }\n\n if (sanitized.length > MAX_TTS_TEXT_LENGTH) {\n throw new Error(\n `TTS text exceeds maximum length of ${MAX_TTS_TEXT_LENGTH} characters (got ${sanitized.length})`\n );\n }\n\n return sanitized;\n}\n","/**\n * ElevenLabs TTS Client for AgentForge.\n *\n * Sends text to the ElevenLabs text-to-speech API and returns audio data.\n * API Reference: https://elevenlabs.io/docs/api-reference/text-to-speech\n */\n\nimport { DEFAULT_VOICE_CONFIG, sanitizeTtsText } from './voice-config.js';\nimport type { VoiceConfig } from './voice-config.js';\n\nconst ELEVENLABS_API_BASE = 'https://api.elevenlabs.io/v1';\n\nexport interface TtsRequest {\n /** Text to convert to speech */\n text: string;\n /** Voice configuration overrides */\n config?: Partial<VoiceConfig>;\n}\n\nexport interface TtsResponse {\n /** Audio data as ArrayBuffer */\n audio: ArrayBuffer;\n /** Content type of the audio (e.g., 'audio/mpeg') */\n contentType: string;\n /** Character count of the input text */\n characterCount: number;\n}\n\n/**\n * Convert text to speech using the ElevenLabs API.\n *\n * @throws Error if ELEVENLABS_API_KEY is not set\n * @throws Error if the API request fails\n * @throws Error if text validation fails\n */\nexport async function textToSpeech(request: TtsRequest): Promise<TtsResponse> {\n const apiKey = process.env.ELEVENLABS_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'ELEVENLABS_API_KEY environment variable is required for TTS. ' +\n 'Get your key at https://elevenlabs.io'\n );\n }\n\n const sanitizedText = sanitizeTtsText(request.text);\n\n const config = {\n voiceId: request.config?.voiceId ?? DEFAULT_VOICE_CONFIG.voiceId,\n model: request.config?.model ?? DEFAULT_VOICE_CONFIG.model,\n speed: request.config?.speed ?? DEFAULT_VOICE_CONFIG.speed,\n language: request.config?.language ?? DEFAULT_VOICE_CONFIG.language,\n };\n\n const url = `${ELEVENLABS_API_BASE}/text-to-speech/${config.voiceId}`;\n\n const body = {\n text: sanitizedText,\n model_id: config.model,\n voice_settings: {\n stability: 0.5,\n similarity_boost: 0.75,\n speed: config.speed,\n },\n ...(config.language !== 'en' ? { language_code: config.language } : {}),\n };\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'xi-api-key': apiKey,\n 'Accept': 'audio/mpeg',\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new Error(\n `ElevenLabs TTS API error (${response.status}): ${errorText}`\n );\n }\n\n const audio = await response.arrayBuffer();\n const contentType = response.headers.get('content-type') || 'audio/mpeg';\n\n return {\n audio,\n contentType,\n characterCount: sanitizedText.length,\n };\n}\n","/**\n * OpenAI Whisper STT Client for AgentForge.\n *\n * Sends audio data to the OpenAI Whisper transcription API.\n * API Reference: https://platform.openai.com/docs/guides/speech-to-text\n */\n\nimport { MAX_STT_FILE_SIZE } from './voice-config.js';\n\nconst OPENAI_API_BASE = 'https://api.openai.com/v1';\n\nexport interface SttRequest {\n /** Audio data as ArrayBuffer or Uint8Array (cross-platform; no Node.js Buffer) */\n audio: ArrayBuffer | Uint8Array;\n /** Audio file name with extension (used for MIME type detection). Default: 'audio.ogg' */\n fileName?: string;\n /** Language hint (ISO-639-1 code). Optional. */\n language?: string;\n /** Whisper model to use. Default: 'whisper-1' */\n model?: string;\n}\n\nexport interface SttResponse {\n /** Transcribed text */\n text: string;\n /** Language detected or used */\n language?: string;\n}\n\n/**\n * Transcribe audio to text using the OpenAI Whisper API.\n *\n * @throws Error if OPENAI_API_KEY is not set\n * @throws Error if the API request fails\n * @throws Error if audio exceeds size limit\n */\nexport async function speechToText(request: SttRequest): Promise<SttResponse> {\n const apiKey = process.env.OPENAI_API_KEY;\n if (!apiKey) {\n throw new Error(\n 'OPENAI_API_KEY environment variable is required for STT. ' +\n 'Get your key at https://platform.openai.com'\n );\n }\n\n // Normalize to a plain ArrayBuffer (cross-platform — no SharedArrayBuffer, no Node Buffer)\n const audioBuffer: ArrayBuffer = (() => {\n if (request.audio instanceof ArrayBuffer) return request.audio;\n const copy = new ArrayBuffer(request.audio.byteLength);\n new Uint8Array(copy).set(request.audio);\n return copy;\n })();\n\n if (audioBuffer.byteLength > MAX_STT_FILE_SIZE) {\n throw new Error(\n `Audio file exceeds maximum size of ${MAX_STT_FILE_SIZE / (1024 * 1024)}MB ` +\n `(got ${(audioBuffer.byteLength / (1024 * 1024)).toFixed(1)}MB)`\n );\n }\n\n if (audioBuffer.byteLength === 0) {\n throw new Error('Audio data is empty');\n }\n\n const fileName = request.fileName || 'audio.ogg';\n const model = request.model || 'whisper-1';\n\n // Build multipart form data\n const blob = new Blob([audioBuffer], { type: getMimeType(fileName) });\n const formData = new FormData();\n formData.append('file', blob, fileName);\n formData.append('model', model);\n\n if (request.language) {\n formData.append('language', request.language);\n }\n\n const response = await fetch(`${OPENAI_API_BASE}/audio/transcriptions`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${apiKey}`,\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => 'Unknown error');\n throw new Error(\n `OpenAI Whisper STT API error (${response.status}): ${errorText}`\n );\n }\n\n const result = await response.json() as { text: string; language?: string };\n\n return {\n text: result.text,\n language: result.language ?? request.language,\n };\n}\n\nfunction getMimeType(fileName: string): string {\n const ext = fileName.split('.').pop()?.toLowerCase();\n const mimeMap: Record<string, string> = {\n mp3: 'audio/mpeg',\n mp4: 'audio/mp4',\n mpeg: 'audio/mpeg',\n mpga: 'audio/mpeg',\n m4a: 'audio/mp4',\n wav: 'audio/wav',\n webm: 'audio/webm',\n ogg: 'audio/ogg',\n oga: 'audio/ogg',\n flac: 'audio/flac',\n };\n return mimeMap[ext || ''] || 'audio/ogg';\n}\n","/**\n * Mastra Voice Tool for AgentForge.\n *\n * Wraps ElevenLabs TTS and OpenAI Whisper STT as a Mastra-compatible tool.\n */\n\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\nimport { textToSpeech } from './tts-client.js';\nimport { speechToText } from './stt-client.js';\nimport type { VoiceConfig } from './voice-config.js';\n\n/**\n * Create a Mastra voice tool that provides TTS and STT capabilities.\n *\n * @param defaultConfig - Default voice configuration for TTS\n * @returns A Mastra tool with text-to-speech and speech-to-text actions\n *\n * @example\n * ```typescript\n * import { createVoiceTool } from '@agentforge-ai/tools-voice';\n *\n * const voiceTool = createVoiceTool({ voiceId: 'custom-voice-id' });\n * // Register with Mastra agent\n * ```\n */\nexport function createVoiceTool(defaultConfig?: Partial<VoiceConfig>) {\n return createTool({\n id: 'agentforge-voice',\n description:\n 'Voice capabilities: convert text to speech (TTS) using ElevenLabs, ' +\n 'or transcribe speech to text (STT) using OpenAI Whisper.',\n inputSchema: z.object({\n action: z.enum(['text-to-speech', 'speech-to-text']).describe(\n 'The voice action to perform'\n ),\n text: z.string().optional().describe(\n 'Text to convert to speech (required for text-to-speech action)'\n ),\n audioBase64: z.string().optional().describe(\n 'Base64-encoded audio data (required for speech-to-text action)'\n ),\n fileName: z.string().optional().describe(\n 'Audio file name with extension for STT (e.g., \"audio.ogg\")'\n ),\n language: z.string().optional().describe(\n 'Language code (e.g., \"en\", \"es\"). Used for both TTS language and STT hint.'\n ),\n voiceId: z.string().optional().describe(\n 'ElevenLabs voice ID override'\n ),\n speed: z.number().min(0.5).max(2.0).optional().describe(\n 'Speech speed multiplier (0.5–2.0)'\n ),\n }),\n outputSchema: z.object({\n success: z.boolean(),\n action: z.string(),\n text: z.string().optional(),\n audioBase64: z.string().optional(),\n contentType: z.string().optional(),\n characterCount: z.number().optional(),\n error: z.string().optional(),\n }),\n execute: async (inputData) => {\n try {\n if (inputData.action === 'text-to-speech') {\n if (!inputData.text) {\n return {\n success: false,\n action: 'text-to-speech',\n error: 'Text is required for text-to-speech action',\n };\n }\n\n const result = await textToSpeech({\n text: inputData.text,\n config: {\n ...defaultConfig,\n ...(inputData.voiceId ? { voiceId: inputData.voiceId } : {}),\n ...(inputData.speed ? { speed: inputData.speed } : {}),\n ...(inputData.language ? { language: inputData.language } : {}),\n },\n });\n\n // Convert ArrayBuffer to base64 (Web Crypto-compatible, no Buffer)\n const bytes = new Uint8Array(result.audio);\n let binary = '';\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]!);\n }\n const audioBase64 = btoa(binary);\n\n return {\n success: true,\n action: 'text-to-speech',\n audioBase64,\n contentType: result.contentType,\n characterCount: result.characterCount,\n };\n }\n\n if (inputData.action === 'speech-to-text') {\n if (!inputData.audioBase64) {\n return {\n success: false,\n action: 'speech-to-text',\n error: 'audioBase64 is required for speech-to-text action',\n };\n }\n\n // Decode base64 to Uint8Array (cross-platform — no Buffer)\n const binaryString = atob(inputData.audioBase64);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n\n const result = await speechToText({\n audio: bytes,\n fileName: inputData.fileName,\n language: inputData.language,\n });\n\n return {\n success: true,\n action: 'speech-to-text',\n text: result.text,\n };\n }\n\n return {\n success: false,\n action: inputData.action,\n error: `Unknown action: ${inputData.action}`,\n };\n } catch (error) {\n return {\n success: false,\n action: inputData.action,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n },\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @agentforge-ai/tools-voice\n *\n * @deprecated Voice functionality has been merged into @agentforge-ai/core.\n * This package is now a thin re-export wrapper for backward compatibility.\n *\n * Migrate your imports:\n * // Before\n * import { createVoiceTool } from '@agentforge-ai/tools-voice';\n * // After\n * import { createVoiceTool } from '@agentforge-ai/core';\n */\nexport * from '@agentforge-ai/core';\n"],"mappings":";AAYA,cAAc;","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentforge-ai/tools-voice",
3
- "version": "0.7.1",
4
- "description": "Voice tools for AgentForge ElevenLabs TTS + OpenAI Whisper STT",
3
+ "version": "0.7.3",
4
+ "description": "Deprecated: voice tools merged into @agentforge-ai/core. This package is a backward-compat re-export wrapper.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "type": "module",
@@ -16,8 +16,7 @@
16
16
  "README.md"
17
17
  ],
18
18
  "dependencies": {
19
- "@mastra/core": "^1.5.0",
20
- "zod": "^3.23.0"
19
+ "@agentforge-ai/core": "0.7.3"
21
20
  },
22
21
  "devDependencies": {
23
22
  "tsup": "^8.0.0",