Fix OpenAI speech streaming after SDK upgrade

This commit is contained in:
Mohamed Boudra
2026-06-08 14:03:15 +07:00
parent ed8a6fefad
commit aab1435cb3
4 changed files with 70 additions and 3 deletions

2
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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");
});
});

View File

@@ -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");