mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Fix OpenAI speech streaming after SDK upgrade
This commit is contained in:
2
package-lock.json
generated
2
package-lock.json
generated
@@ -38150,7 +38150,7 @@
|
||||
"version": "0.1.91-beta.2",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.17.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.168",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.168",
|
||||
"@anthropic-ai/sdk": "^0.102.0",
|
||||
"@getpaseo/client": "0.1.91-beta.2",
|
||||
"@getpaseo/highlight": "0.1.91-beta.2",
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.17.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.168",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.3.168",
|
||||
"@anthropic-ai/sdk": "^0.102.0",
|
||||
"@getpaseo/client": "0.1.91-beta.2",
|
||||
"@getpaseo/highlight": "0.1.91-beta.2",
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import type pino from "pino";
|
||||
import { ReadableStream } from "node:stream/web";
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
const openAiMocks = vi.hoisted(() => ({
|
||||
createSpeech: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("openai", () => ({
|
||||
OpenAI: vi.fn().mockImplementation(
|
||||
class {
|
||||
audio = {
|
||||
speech: {
|
||||
create: openAiMocks.createSpeech,
|
||||
},
|
||||
};
|
||||
},
|
||||
),
|
||||
}));
|
||||
|
||||
import { OpenAITTS } from "./tts.js";
|
||||
|
||||
function createLogger(): pino.Logger {
|
||||
const logger = {
|
||||
child: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
error: vi.fn(),
|
||||
info: vi.fn(),
|
||||
};
|
||||
|
||||
logger.child.mockReturnValue(logger);
|
||||
|
||||
return logger as unknown as pino.Logger;
|
||||
}
|
||||
|
||||
describe("OpenAITTS", () => {
|
||||
beforeEach(() => {
|
||||
openAiMocks.createSpeech.mockReset();
|
||||
});
|
||||
|
||||
test("returns a Node stream from the OpenAI web response body", async () => {
|
||||
openAiMocks.createSpeech.mockResolvedValue({
|
||||
body: new ReadableStream<Uint8Array>({
|
||||
start(controller) {
|
||||
controller.enqueue(Buffer.from("audio"));
|
||||
controller.close();
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const tts = new OpenAITTS({ apiKey: "sk-test" }, createLogger());
|
||||
const result = await tts.synthesizeSpeech("hello");
|
||||
|
||||
const chunks: Buffer[] = [];
|
||||
for await (const chunk of result.stream) {
|
||||
chunks.push(Buffer.from(chunk));
|
||||
}
|
||||
|
||||
expect(Buffer.concat(chunks).toString()).toBe("audio");
|
||||
expect(result.format).toBe("pcm");
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
import type pino from "pino";
|
||||
import { OpenAI } from "openai";
|
||||
import { Readable } from "node:stream";
|
||||
import type { ReadableStream as NodeReadableStream } from "node:stream/web";
|
||||
import type { SpeechStreamResult, TextToSpeechProvider } from "../../speech-provider.js";
|
||||
|
||||
export type { SpeechStreamResult };
|
||||
@@ -65,7 +66,11 @@ export class OpenAITTS implements TextToSpeechProvider {
|
||||
| "pcm",
|
||||
});
|
||||
|
||||
const audioStream = response.body as unknown as Readable;
|
||||
if (!response.body) {
|
||||
throw new Error("OpenAI speech response did not include an audio stream");
|
||||
}
|
||||
|
||||
const audioStream = Readable.fromWeb(response.body as NodeReadableStream<Uint8Array>);
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
this.logger.debug({ duration }, "Speech synthesis stream ready");
|
||||
|
||||
Reference in New Issue
Block a user