@mastra/voice-speechify 0.0.0-storage-20250225005900 → 0.0.0-vnextWorkflows-20250416071310
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/{LICENSE → LICENSE.md} +3 -1
- package/dist/_tsup-dts-rollup.d.cts +33 -0
- package/dist/index.cjs +793 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.js +6 -2
- package/package.json +20 -12
- package/.turbo/turbo-build.log +0 -19
- package/CHANGELOG.md +0 -35
- package/eslint.config.js +0 -6
- package/src/index.test.ts +0 -101
- package/src/index.ts +0 -103
- package/src/voices.ts +0 -711
- package/tsconfig.json +0 -5
- package/vitest.config.ts +0 -8
package/dist/index.d.cts
ADDED
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { Readable } from 'stream';
|
|
1
2
|
import { MastraVoice } from '@mastra/core/voice';
|
|
2
3
|
import { Speechify } from '@speechify/api-sdk';
|
|
3
|
-
import { Readable } from 'stream';
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
6
|
|
|
@@ -744,7 +744,11 @@ var SpeechifyVoice = class extends MastraVoice {
|
|
|
744
744
|
async streamToString(stream) {
|
|
745
745
|
const chunks = [];
|
|
746
746
|
for await (const chunk of stream) {
|
|
747
|
-
|
|
747
|
+
if (typeof chunk === "string") {
|
|
748
|
+
chunks.push(Buffer.from(chunk));
|
|
749
|
+
} else {
|
|
750
|
+
chunks.push(chunk);
|
|
751
|
+
}
|
|
748
752
|
}
|
|
749
753
|
return Buffer.concat(chunks).toString("utf-8");
|
|
750
754
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/voice-speechify",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-vnextWorkflows-20250416071310",
|
|
4
4
|
"description": "Mastra Speechify voice integration",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
6
9
|
"main": "dist/index.js",
|
|
7
10
|
"types": "dist/index.d.ts",
|
|
8
11
|
"exports": {
|
|
@@ -10,26 +13,31 @@
|
|
|
10
13
|
"import": {
|
|
11
14
|
"types": "./dist/index.d.ts",
|
|
12
15
|
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"require": {
|
|
18
|
+
"types": "./dist/index.d.cts",
|
|
19
|
+
"default": "./dist/index.cjs"
|
|
13
20
|
}
|
|
14
21
|
},
|
|
15
22
|
"./package.json": "./package.json"
|
|
16
23
|
},
|
|
24
|
+
"license": "Elastic-2.0",
|
|
17
25
|
"dependencies": {
|
|
18
|
-
"@speechify/api-sdk": "^2.
|
|
19
|
-
"zod": "^3.24.
|
|
20
|
-
"@mastra/core": "
|
|
26
|
+
"@speechify/api-sdk": "^2.4.1",
|
|
27
|
+
"zod": "^3.24.2",
|
|
28
|
+
"@mastra/core": "0.0.0-vnextWorkflows-20250416071310"
|
|
21
29
|
},
|
|
22
30
|
"devDependencies": {
|
|
23
|
-
"@microsoft/api-extractor": "^7.
|
|
24
|
-
"@types/node": "^
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"@internal/lint": "0.0.
|
|
31
|
+
"@microsoft/api-extractor": "^7.52.2",
|
|
32
|
+
"@types/node": "^20.17.27",
|
|
33
|
+
"eslint": "^9.23.0",
|
|
34
|
+
"tsup": "^8.4.0",
|
|
35
|
+
"typescript": "^5.8.2",
|
|
36
|
+
"vitest": "^2.1.9",
|
|
37
|
+
"@internal/lint": "0.0.2"
|
|
30
38
|
},
|
|
31
39
|
"scripts": {
|
|
32
|
-
"build": "tsup src/index.ts --format esm --experimental-dts --clean --treeshake",
|
|
40
|
+
"build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
|
|
33
41
|
"build:watch": "pnpm build --watch",
|
|
34
42
|
"lint": "eslint .",
|
|
35
43
|
"test": "vitest run"
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
> @mastra/voice-speechify@0.1.0-alpha.2 build /Users/ward/projects/mastra/mastra/voice/speechify
|
|
4
|
-
> tsup src/index.ts --format esm --experimental-dts --clean --treeshake
|
|
5
|
-
|
|
6
|
-
[34mCLI[39m Building entry: src/index.ts
|
|
7
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
8
|
-
[34mCLI[39m tsup v8.3.6
|
|
9
|
-
[34mTSC[39m Build start
|
|
10
|
-
[32mTSC[39m ⚡️ Build success in 1387ms
|
|
11
|
-
[34mDTS[39m Build start
|
|
12
|
-
[34mCLI[39m Target: es2022
|
|
13
|
-
Analysis will use the bundled TypeScript version 5.7.3
|
|
14
|
-
[36mWriting package typings: /Users/ward/projects/mastra/mastra/voice/speechify/dist/_tsup-dts-rollup.d.ts[39m
|
|
15
|
-
[32mDTS[39m ⚡️ Build success in 1524ms
|
|
16
|
-
[34mCLI[39m Cleaning output folder
|
|
17
|
-
[34mESM[39m Build start
|
|
18
|
-
[32mESM[39m [1mdist/index.js [22m[32m10.28 KB[39m
|
|
19
|
-
[32mESM[39m ⚡️ Build success in 166ms
|
package/CHANGELOG.md
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# @mastra/voice-speechify
|
|
2
|
-
|
|
3
|
-
## 0.0.0-storage-20250225005900
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- f477df7: deprecate @mastra/speech-speechify for @mastra/voice-speechify
|
|
8
|
-
- Updated dependencies [7fceae1]
|
|
9
|
-
- Updated dependencies [f626fbb]
|
|
10
|
-
- Updated dependencies [8db2a28]
|
|
11
|
-
- @mastra/core@0.0.0-storage-20250225005900
|
|
12
|
-
|
|
13
|
-
## 0.1.0-alpha.2
|
|
14
|
-
|
|
15
|
-
### Patch Changes
|
|
16
|
-
|
|
17
|
-
- f477df7: deprecate @mastra/speech-speechify for @mastra/voice-speechify
|
|
18
|
-
|
|
19
|
-
## 0.1.0 (2024-XX-XX)
|
|
20
|
-
|
|
21
|
-
This package replaces the deprecated @mastra/speech-speechify package. All functionality has been migrated to this new package with a more consistent naming scheme.
|
|
22
|
-
|
|
23
|
-
### Changes from @mastra/speech-speechify
|
|
24
|
-
|
|
25
|
-
- Package renamed from @mastra/speech-speechify to @mastra/voice-speechify
|
|
26
|
-
- API changes:
|
|
27
|
-
- `SpeechifyTTS` class renamed to `SpeechifyVoice`
|
|
28
|
-
- `generate()` and `stream()` methods combined into `speak()`
|
|
29
|
-
- `voices()` method renamed to `getSpeakers()`
|
|
30
|
-
- Constructor configuration simplified
|
|
31
|
-
- Added support for text stream input
|
|
32
|
-
- All core functionality remains the same
|
|
33
|
-
- Import paths should be updated from '@mastra/speech-speechify' to '@mastra/voice-speechify'
|
|
34
|
-
|
|
35
|
-
For a complete history of changes prior to the rename, please see the changelog of the original package.
|
package/eslint.config.js
DELETED
package/src/index.test.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { writeFileSync, mkdirSync } from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { Readable } from 'stream';
|
|
4
|
-
import { describe, expect, it, beforeAll } from 'vitest';
|
|
5
|
-
|
|
6
|
-
import { SpeechifyVoice } from './index';
|
|
7
|
-
|
|
8
|
-
describe('SpeechifyVoice Integration Tests', () => {
|
|
9
|
-
let voice: SpeechifyVoice;
|
|
10
|
-
const outputDir = path.join(process.cwd(), 'test-outputs');
|
|
11
|
-
|
|
12
|
-
beforeAll(() => {
|
|
13
|
-
// Create output directory if it doesn't exist
|
|
14
|
-
try {
|
|
15
|
-
mkdirSync(outputDir, { recursive: true });
|
|
16
|
-
} catch (err) {
|
|
17
|
-
console.error('Failed to create output directory', err);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
voice = new SpeechifyVoice({
|
|
21
|
-
speechModel: {
|
|
22
|
-
name: 'simba-multilingual',
|
|
23
|
-
},
|
|
24
|
-
speaker: 'george',
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe('voices', () => {
|
|
29
|
-
it('should list available voices', async () => {
|
|
30
|
-
const voices = await voice.getSpeakers();
|
|
31
|
-
expect(voices).toBeInstanceOf(Array);
|
|
32
|
-
expect(voices.length).toBeGreaterThan(0);
|
|
33
|
-
expect(voices[0]).toHaveProperty('voiceId');
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe('speech', () => {
|
|
38
|
-
it('should generate audio and save to file from text input', async () => {
|
|
39
|
-
const result = await voice.speak('Hello from Mastra Voice - Speechify');
|
|
40
|
-
const outputPath = path.join(outputDir, 'speechify-speech-test.mp3');
|
|
41
|
-
const chunks: Buffer[] = [];
|
|
42
|
-
for await (const chunk of result) {
|
|
43
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const audioBuffer = Buffer.concat(chunks);
|
|
47
|
-
expect(audioBuffer.length).toBeGreaterThan(0);
|
|
48
|
-
writeFileSync(outputPath, audioBuffer);
|
|
49
|
-
}, 10000);
|
|
50
|
-
|
|
51
|
-
it('should work with different parameters', async () => {
|
|
52
|
-
const result = await voice.speak('Test with parameters', { speaker: 'george' });
|
|
53
|
-
const chunks: Buffer[] = [];
|
|
54
|
-
for await (const chunk of result) {
|
|
55
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
56
|
-
}
|
|
57
|
-
const audioBuffer = Buffer.concat(chunks);
|
|
58
|
-
const outputPath = path.join(outputDir, 'speechify-speech-test-params.mp3');
|
|
59
|
-
writeFileSync(outputPath, audioBuffer);
|
|
60
|
-
}, 10000);
|
|
61
|
-
|
|
62
|
-
it('should generate audio from a stream input', async () => {
|
|
63
|
-
const inputStream = Readable.from(['Hello from stream input - Speechify']);
|
|
64
|
-
const result = await voice.speak(inputStream);
|
|
65
|
-
const chunks: Buffer[] = [];
|
|
66
|
-
for await (const chunk of result) {
|
|
67
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const audioBuffer = Buffer.concat(chunks);
|
|
71
|
-
expect(audioBuffer.length).toBeGreaterThan(0);
|
|
72
|
-
const outputPath = path.join(outputDir, 'speechify-speech-test-stream.mp3');
|
|
73
|
-
writeFileSync(outputPath, audioBuffer);
|
|
74
|
-
}, 10000);
|
|
75
|
-
|
|
76
|
-
it('should generate audio using default SpeechifyVoice instance', async () => {
|
|
77
|
-
const defaultVoice = new SpeechifyVoice();
|
|
78
|
-
const result = await defaultVoice.speak('Hello from default SpeechifyVoice instance');
|
|
79
|
-
const chunks: Buffer[] = [];
|
|
80
|
-
for await (const chunk of result) {
|
|
81
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const audioBuffer = Buffer.concat(chunks);
|
|
85
|
-
expect(audioBuffer.length).toBeGreaterThan(0);
|
|
86
|
-
const outputPath = path.join(outputDir, 'speechify-speech-test-default.mp3');
|
|
87
|
-
writeFileSync(outputPath, audioBuffer);
|
|
88
|
-
}, 10000);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// Error cases
|
|
92
|
-
describe('error handling', () => {
|
|
93
|
-
it('should handle empty text', async () => {
|
|
94
|
-
await expect(voice.speak('')).rejects.toThrow();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should handle invalid voice', async () => {
|
|
98
|
-
await expect(voice.speak('Test', { speaker: 'invalid_voice' })).rejects.toThrow();
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { MastraVoice } from '@mastra/core/voice';
|
|
2
|
-
import { Speechify } from '@speechify/api-sdk';
|
|
3
|
-
import type { AudioStreamRequest, VoiceModelName } from '@speechify/api-sdk';
|
|
4
|
-
import { Readable } from 'stream';
|
|
5
|
-
|
|
6
|
-
import { SPEECHIFY_VOICES } from './voices';
|
|
7
|
-
import type { SpeechifyVoiceId } from './voices';
|
|
8
|
-
|
|
9
|
-
interface SpeechifyConfig {
|
|
10
|
-
name?: VoiceModelName;
|
|
11
|
-
apiKey?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export class SpeechifyVoice extends MastraVoice {
|
|
15
|
-
private client: Speechify;
|
|
16
|
-
|
|
17
|
-
constructor({ speechModel, speaker }: { speechModel?: SpeechifyConfig; speaker?: SpeechifyVoiceId } = {}) {
|
|
18
|
-
super({
|
|
19
|
-
speechModel: {
|
|
20
|
-
name: speechModel?.name ?? 'simba-english',
|
|
21
|
-
apiKey: speechModel?.apiKey ?? process.env.SPEECHIFY_API_KEY,
|
|
22
|
-
},
|
|
23
|
-
speaker: speaker ?? 'george',
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const apiKey = speechModel?.apiKey ?? process.env.SPEECHIFY_API_KEY;
|
|
27
|
-
if (!apiKey) {
|
|
28
|
-
throw new Error('SPEECHIFY_API_KEY is not set');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
this.client = new Speechify({ apiKey });
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async getSpeakers() {
|
|
35
|
-
return this.traced(
|
|
36
|
-
() =>
|
|
37
|
-
SPEECHIFY_VOICES.map(voice => ({
|
|
38
|
-
voiceId: voice,
|
|
39
|
-
name: voice,
|
|
40
|
-
})),
|
|
41
|
-
'voice.speechify.voices',
|
|
42
|
-
)();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
private async streamToString(stream: NodeJS.ReadableStream): Promise<string> {
|
|
46
|
-
const chunks: Buffer[] = [];
|
|
47
|
-
for await (const chunk of stream) {
|
|
48
|
-
chunks.push(Buffer.from(chunk));
|
|
49
|
-
}
|
|
50
|
-
return Buffer.concat(chunks).toString('utf-8');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async speak(
|
|
54
|
-
input: string | NodeJS.ReadableStream,
|
|
55
|
-
options?: {
|
|
56
|
-
speaker?: string;
|
|
57
|
-
} & Omit<AudioStreamRequest, 'voiceId' | 'input'>,
|
|
58
|
-
): Promise<NodeJS.ReadableStream> {
|
|
59
|
-
const text = typeof input === 'string' ? input : await this.streamToString(input);
|
|
60
|
-
|
|
61
|
-
return this.traced(async () => {
|
|
62
|
-
const request: AudioStreamRequest = {
|
|
63
|
-
input: text,
|
|
64
|
-
model: (options?.model || this.speechModel?.name) as VoiceModelName,
|
|
65
|
-
voiceId: (options?.speaker || this.speaker) as SpeechifyVoiceId,
|
|
66
|
-
...options,
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const webStream = await this.client.audioStream(request);
|
|
70
|
-
const reader = webStream.getReader();
|
|
71
|
-
|
|
72
|
-
const nodeStream = new Readable({
|
|
73
|
-
read: async function () {
|
|
74
|
-
try {
|
|
75
|
-
const { done, value } = await reader.read();
|
|
76
|
-
if (done) {
|
|
77
|
-
this.push(null);
|
|
78
|
-
} else {
|
|
79
|
-
this.push(value);
|
|
80
|
-
}
|
|
81
|
-
} catch (error) {
|
|
82
|
-
this.destroy(error as Error);
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
nodeStream.on('end', () => {
|
|
88
|
-
reader.releaseLock();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
return nodeStream;
|
|
92
|
-
}, 'voice.speechify.speak')();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
async listen(
|
|
96
|
-
_input: NodeJS.ReadableStream,
|
|
97
|
-
_options?: Record<string, unknown>,
|
|
98
|
-
): Promise<string | NodeJS.ReadableStream> {
|
|
99
|
-
throw new Error('Speechify does not support speech recognition');
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export type { SpeechifyConfig, SpeechifyVoiceId };
|