@pipedog/openai 0.0.0 → 0.4.0

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/tts.d.ts ADDED
@@ -0,0 +1,86 @@
1
+ import { type Frame, type StartFrame } from "@pipedog/core/frames/frames";
2
+ import { TTSSettings, type TTSSettingsOptions } from "@pipedog/core/services/settings";
3
+ import { TTSService, type TTSServiceOptions } from "@pipedog/core/services/tts_service";
4
+ import { type NotGiven } from "@pipedog/core/utils/types";
5
+ import OpenAI from "openai";
6
+ export type ValidVoice = "alloy" | "ash" | "ballad" | "cedar" | "coral" | "echo" | "fable" | "marin" | "nova" | "onyx" | "sage" | "shimmer" | "verse";
7
+ export declare const VALID_VOICES: Readonly<Record<string, ValidVoice>>;
8
+ export interface OpenAITTSSettingsOptions extends TTSSettingsOptions {
9
+ /** Instructions to guide voice synthesis behavior. */
10
+ instructions?: string | null | NotGiven;
11
+ /** Voice speed control (0.25 to 4.0, default 1.0). */
12
+ speed?: number | null | NotGiven;
13
+ }
14
+ /**
15
+ * Settings for OpenAITTSService.
16
+ *
17
+ * - `instructions`: instructions to guide voice synthesis behavior.
18
+ * - `speed`: voice speed control (0.25 to 4.0, default 1.0).
19
+ */
20
+ export declare class OpenAITTSSettings extends TTSSettings {
21
+ instructions: string | null | NotGiven;
22
+ speed: number | null | NotGiven;
23
+ constructor(options?: OpenAITTSSettingsOptions);
24
+ }
25
+ export interface OpenAITTSServiceOptions extends TTSServiceOptions {
26
+ /** OpenAI API key for authentication. If null, uses environment variable. */
27
+ apiKey?: string | null;
28
+ /** Custom base URL for OpenAI API. If null, uses default. */
29
+ baseUrl?: string | null;
30
+ /**
31
+ * Custom `fetch` for API requests, e.g. one with a longer request timeout.
32
+ * Defaults to null, which lets the SDK use its own.
33
+ *
34
+ * DIVERGENCE: upstream takes an httpx client here.
35
+ */
36
+ httpClient?: typeof fetch | null;
37
+ /** Runtime-updatable settings. */
38
+ settings?: OpenAITTSSettings | null;
39
+ }
40
+ /**
41
+ * OpenAI Text-to-Speech service that generates audio from text.
42
+ *
43
+ * This service uses the OpenAI TTS API to generate PCM-encoded audio at
44
+ * 24kHz. Supports multiple voice models and configurable parameters for
45
+ * high-quality speech synthesis with streaming audio output.
46
+ */
47
+ export declare class OpenAITTSService extends TTSService {
48
+ static Settings: typeof OpenAITTSSettings;
49
+ protected _settings: OpenAITTSSettings;
50
+ protected _client: OpenAI;
51
+ /**
52
+ * Initialize OpenAI TTS service.
53
+ *
54
+ * @param options.apiKey OpenAI API key for authentication. If null, uses
55
+ * environment variable.
56
+ * @param options.baseUrl Custom base URL for OpenAI API. If null, uses default.
57
+ * @param options.httpClient Custom `fetch` for API requests.
58
+ * @param options.sampleRate Output audio sample rate in Hz. If null, uses
59
+ * OpenAI's default 24kHz.
60
+ * @param options.settings Runtime-updatable settings. Its given fields
61
+ * override the defaults (model "gpt-4o-mini-tts", voice "alloy").
62
+ * Other options are passed to TTSService.
63
+ */
64
+ constructor(options?: OpenAITTSServiceOptions);
65
+ /**
66
+ * Check if this service can generate processing metrics.
67
+ *
68
+ * @returns True, as OpenAI TTS service supports metrics generation.
69
+ */
70
+ canGenerateMetrics(): boolean;
71
+ /**
72
+ * Start the OpenAI TTS service.
73
+ *
74
+ * @param frame The start frame containing initialization parameters.
75
+ */
76
+ start(frame: StartFrame): Promise<void>;
77
+ /**
78
+ * Generate speech from text using OpenAI's TTS API.
79
+ *
80
+ * @param text The text to synthesize into speech.
81
+ * @param contextId The context ID for tracking audio frames.
82
+ * @returns Audio frames containing the synthesized speech data.
83
+ */
84
+ runTts(text: string, contextId: string): AsyncGenerator<Frame, void>;
85
+ }
86
+ //# sourceMappingURL=tts.d.ts.map
package/dist/tts.js ADDED
@@ -0,0 +1,238 @@
1
+ // Copyright (c) 2024-2026, Daily
2
+ // Copyright (c) 2026, pipedog contributors
3
+ // SPDX-License-Identifier: BSD-2-Clause
4
+ //
5
+ // Ported from pipecat: src/pipecat/services/openai/tts.py
6
+ /**
7
+ * OpenAI text-to-speech service implementation.
8
+ *
9
+ * This module provides integration with OpenAI's text-to-speech API for
10
+ * generating high-quality synthetic speech from text input.
11
+ *
12
+ * Porting notes:
13
+ * - The client is `OpenAI` from the `openai` JS SDK (fetch based). The
14
+ * streamed body is read from the `Response` the SDK returns, re-chunked to
15
+ * `chunkSize` bytes as `iter_bytes(chunk_size)` does.
16
+ * - `httpClient` is the SDK's `fetch` option (upstream: an httpx client).
17
+ * - Deprecated APIs are not ported: `OpenAITTSService.InputParams` and the
18
+ * `voice`, `model`, `instructions`, `speed` and `params` constructor
19
+ * options.
20
+ */
21
+ import { abortable, currentSignal, rethrowIfCancelled } from "@pipedog/asyncio";
22
+ import { ErrorFrame, TTSAudioRawFrame, } from "@pipedog/core/frames/frames";
23
+ import { TTSSettings } from "@pipedog/core/services/settings";
24
+ import { TTSService } from "@pipedog/core/services/tts_service";
25
+ import { logger } from "@pipedog/core/utils/logger";
26
+ import { installTracedTts } from "@pipedog/core/utils/tracing/service_decorators";
27
+ import { assertGiven, NOT_GIVEN } from "@pipedog/core/utils/types";
28
+ import OpenAI, { BadRequestError } from "openai";
29
+ import { OPENAI_SAMPLE_RATE } from "./_constants.js";
30
+ export const VALID_VOICES = {
31
+ alloy: "alloy",
32
+ ash: "ash",
33
+ ballad: "ballad",
34
+ cedar: "cedar",
35
+ coral: "coral",
36
+ echo: "echo",
37
+ fable: "fable",
38
+ marin: "marin",
39
+ nova: "nova",
40
+ onyx: "onyx",
41
+ sage: "sage",
42
+ shimmer: "shimmer",
43
+ verse: "verse",
44
+ };
45
+ /**
46
+ * Settings for OpenAITTSService.
47
+ *
48
+ * - `instructions`: instructions to guide voice synthesis behavior.
49
+ * - `speed`: voice speed control (0.25 to 4.0, default 1.0).
50
+ */
51
+ export class OpenAITTSSettings extends TTSSettings {
52
+ instructions = NOT_GIVEN;
53
+ speed = NOT_GIVEN;
54
+ constructor(options = {}) {
55
+ super(options);
56
+ this._assignOptions(options);
57
+ }
58
+ }
59
+ /** Yield the bytes of a body stream in chunks of exactly `chunkSize` (the last may be shorter). */
60
+ async function* iterBytes(body, chunkSize) {
61
+ if (!body)
62
+ return;
63
+ const reader = body.getReader();
64
+ let buffer = new Uint8Array(0);
65
+ try {
66
+ while (true) {
67
+ const { done, value } = await abortable(reader.read());
68
+ if (done)
69
+ break;
70
+ if (!value || value.length === 0)
71
+ continue;
72
+ const merged = new Uint8Array(buffer.length + value.length);
73
+ merged.set(buffer);
74
+ merged.set(value, buffer.length);
75
+ buffer = merged;
76
+ while (buffer.length >= chunkSize) {
77
+ yield buffer.slice(0, chunkSize);
78
+ buffer = buffer.slice(chunkSize);
79
+ }
80
+ }
81
+ if (buffer.length > 0) {
82
+ yield buffer;
83
+ }
84
+ }
85
+ finally {
86
+ try {
87
+ await reader.cancel();
88
+ }
89
+ catch (e) {
90
+ rethrowIfCancelled(e);
91
+ }
92
+ }
93
+ }
94
+ /**
95
+ * OpenAI Text-to-Speech service that generates audio from text.
96
+ *
97
+ * This service uses the OpenAI TTS API to generate PCM-encoded audio at
98
+ * 24kHz. Supports multiple voice models and configurable parameters for
99
+ * high-quality speech synthesis with streaming audio output.
100
+ */
101
+ export class OpenAITTSService extends TTSService {
102
+ static Settings = OpenAITTSSettings;
103
+ static {
104
+ installTracedTts(OpenAITTSService.prototype, "runTts");
105
+ }
106
+ _client;
107
+ /**
108
+ * Initialize OpenAI TTS service.
109
+ *
110
+ * @param options.apiKey OpenAI API key for authentication. If null, uses
111
+ * environment variable.
112
+ * @param options.baseUrl Custom base URL for OpenAI API. If null, uses default.
113
+ * @param options.httpClient Custom `fetch` for API requests.
114
+ * @param options.sampleRate Output audio sample rate in Hz. If null, uses
115
+ * OpenAI's default 24kHz.
116
+ * @param options.settings Runtime-updatable settings. Its given fields
117
+ * override the defaults (model "gpt-4o-mini-tts", voice "alloy").
118
+ * Other options are passed to TTSService.
119
+ */
120
+ constructor(options = {}) {
121
+ const sampleRate = options.sampleRate ?? null;
122
+ if (sampleRate && sampleRate !== OPENAI_SAMPLE_RATE) {
123
+ logger.warning(`OpenAI TTS only supports ${OPENAI_SAMPLE_RATE}Hz sample rate. ` +
124
+ `Current rate of ${sampleRate}Hz may cause issues.`);
125
+ }
126
+ // 1. Initialize default_settings with hardcoded defaults
127
+ const defaultSettings = new new.target.Settings({
128
+ model: "gpt-4o-mini-tts",
129
+ voice: "alloy",
130
+ language: null,
131
+ instructions: null,
132
+ speed: null,
133
+ });
134
+ // 2. Apply settings delta (canonical API, always wins)
135
+ if (options.settings !== undefined && options.settings !== null) {
136
+ defaultSettings.applyUpdate(options.settings);
137
+ }
138
+ super({
139
+ ...options,
140
+ sampleRate,
141
+ pushStartFrame: true,
142
+ pushStopFrames: true,
143
+ settings: defaultSettings,
144
+ });
145
+ this._client = new OpenAI({
146
+ apiKey: options.apiKey ?? undefined,
147
+ baseURL: options.baseUrl ?? undefined,
148
+ ...(options.httpClient ? { fetch: options.httpClient } : {}),
149
+ });
150
+ }
151
+ /**
152
+ * Check if this service can generate processing metrics.
153
+ *
154
+ * @returns True, as OpenAI TTS service supports metrics generation.
155
+ */
156
+ canGenerateMetrics() {
157
+ return true;
158
+ }
159
+ /**
160
+ * Start the OpenAI TTS service.
161
+ *
162
+ * @param frame The start frame containing initialization parameters.
163
+ */
164
+ async start(frame) {
165
+ await super.start(frame);
166
+ if (this.sampleRate !== OPENAI_SAMPLE_RATE) {
167
+ logger.warning(`OpenAI TTS requires ${OPENAI_SAMPLE_RATE}Hz sample rate. ` +
168
+ `Current rate of ${this.sampleRate}Hz may cause issues.`);
169
+ }
170
+ }
171
+ /**
172
+ * Generate speech from text using OpenAI's TTS API.
173
+ *
174
+ * @param text The text to synthesize into speech.
175
+ * @param contextId The context ID for tracking audio frames.
176
+ * @returns Audio frames containing the synthesized speech data.
177
+ */
178
+ async *runTts(text, contextId) {
179
+ const voice = assertGiven(this._settings.voice);
180
+ if (voice === null) {
181
+ yield new ErrorFrame({ error: "OpenAI TTS voice must be specified" });
182
+ return;
183
+ }
184
+ if (!Object.hasOwn(VALID_VOICES, voice)) {
185
+ yield new ErrorFrame({
186
+ error: `OpenAI TTS voice '${voice}' is not supported ` +
187
+ `(must be one of: ${Object.keys(VALID_VOICES).sort().join(", ")})`,
188
+ });
189
+ return;
190
+ }
191
+ try {
192
+ // Setup API parameters
193
+ const createParams = {
194
+ input: text,
195
+ model: this._settings.model,
196
+ voice: VALID_VOICES[voice],
197
+ response_format: "pcm",
198
+ };
199
+ if (this._settings.instructions) {
200
+ createParams.instructions = this._settings.instructions;
201
+ }
202
+ if (this._settings.speed) {
203
+ createParams.speed = this._settings.speed;
204
+ }
205
+ const r = await abortable(this._client.audio.speech.create(createParams, { signal: currentSignal() }));
206
+ if (r.status !== 200) {
207
+ const error = await abortable(r.text());
208
+ logger.error(`${this} error getting audio (status: ${r.status}, error: ${error})`);
209
+ yield new ErrorFrame({
210
+ error: `Error getting audio (status: ${r.status}, error: ${error})`,
211
+ });
212
+ return;
213
+ }
214
+ await this.startTtsUsageMetrics(text);
215
+ const CHUNK_SIZE = this.chunkSize;
216
+ for await (const chunk of iterBytes(r.body, CHUNK_SIZE)) {
217
+ if (chunk.length > 0) {
218
+ await this.stopTtfbMetrics();
219
+ const frame = new TTSAudioRawFrame({
220
+ audio: chunk,
221
+ sampleRate: this.sampleRate,
222
+ numChannels: 1,
223
+ contextId,
224
+ });
225
+ yield frame;
226
+ }
227
+ }
228
+ }
229
+ catch (e) {
230
+ if (e instanceof BadRequestError) {
231
+ yield new ErrorFrame({ error: `Unknown error occurred: ${e.message}` });
232
+ return;
233
+ }
234
+ throw e;
235
+ }
236
+ }
237
+ }
238
+ //# sourceMappingURL=tts.js.map
package/package.json CHANGED
@@ -1,12 +1,58 @@
1
1
  {
2
2
  "name": "@pipedog/openai",
3
- "version": "0.0.0",
4
- "description": "Placeholder for @pipedog/openai. Releases are published from GitHub Actions; see https://github.com/polvallverdu/pipedog/tree/main/packages/openai#readme",
3
+ "version": "0.4.0",
4
+ "description": "OpenAI services (LLM, Responses, Realtime, Live, STT, TTS, image) for pipedog.",
5
5
  "license": "BSD-2-Clause",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/polvallverdu/pipedog.git",
9
9
  "directory": "packages/openai"
10
10
  },
11
- "homepage": "https://github.com/polvallverdu/pipedog/tree/main/packages/openai#readme"
11
+ "homepage": "https://github.com/polvallverdu/pipedog/tree/main/packages/openai#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/polvallverdu/pipedog/issues"
14
+ },
15
+ "type": "module",
16
+ "sideEffects": false,
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./*": {
23
+ "types": "./dist/*.d.ts",
24
+ "default": "./dist/*.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "LICENSE",
30
+ "NOTICE"
31
+ ],
32
+ "scripts": {
33
+ "test": "bun test"
34
+ },
35
+ "dependencies": {
36
+ "@pipedog/asyncio": "0.4.0",
37
+ "@pipedog/core": "0.4.0",
38
+ "@pipedog/whisper": "0.4.0",
39
+ "fast-png": "^8.0.0",
40
+ "openai": "^6.49.0"
41
+ },
42
+ "pipecat": {
43
+ "runtimes": [
44
+ "bun",
45
+ "node",
46
+ "deno",
47
+ "workerd"
48
+ ],
49
+ "upstream": [
50
+ "src/pipecat/services/openai",
51
+ "src/pipecat/adapters/services/open_ai_live_adapter.py",
52
+ "src/pipecat/adapters/services/open_ai_realtime_adapter.py",
53
+ "src/pipecat/adapters/services/open_ai_responses_adapter.py"
54
+ ]
55
+ },
56
+ "main": "./dist/index.js",
57
+ "types": "./dist/index.d.ts"
12
58
  }
package/README.md DELETED
@@ -1,5 +0,0 @@
1
- # @pipedog/openai
2
-
3
- Placeholder version. Install a released version instead: `npm install @pipedog/openai@latest`.
4
-
5
- Part of [pipedog](https://github.com/polvallverdu/pipedog).