@ai-sdk/openai 4.0.11 → 4.0.13
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/CHANGELOG.md +22 -0
- package/dist/index.js +69 -54
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +61 -49
- package/dist/internal/index.js.map +1 -1
- package/package.json +4 -4
- package/src/openai-provider.ts +7 -4
- package/src/transcription/openai-transcription-model.ts +101 -82
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @ai-sdk/openai
|
|
2
2
|
|
|
3
|
+
## 4.0.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7805e4a: Fix realtime transcription auth header handling: per-call `authorization` headers now override configuration headers regardless of header-key casing (last case-variant wins), and the `Bearer` scheme is matched case-insensitively.
|
|
8
|
+
- cd12954: Reject empty OpenAI, Anthropic, and Replicate base URLs with a helpful AI SDK
|
|
9
|
+
invalid argument error.
|
|
10
|
+
- Updated dependencies [4be62c1]
|
|
11
|
+
- Updated dependencies [7805e4a]
|
|
12
|
+
- Updated dependencies [cd12954]
|
|
13
|
+
- @ai-sdk/provider-utils@5.0.9
|
|
14
|
+
|
|
15
|
+
## 4.0.12
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- e193290: Add `connectToWebSocket` to `@ai-sdk/provider-utils`: a shared WebSocket connect layer (constructor resolution, header hygiene, abort wiring, message decoding) analogous to `postToApi` for HTTP. The openai and xai streaming transcription models now use it instead of hand-rolled connects. For openai and xai this also means WebSocket constructor failures now surface as stream errors instead of throwing synchronously from `doStream`, an already-aborted signal no longer constructs a socket, and the caller's audio stream is cancelled on pre-open failures. Messages are processed in order with close handling deferred behind pending frames, audio send loops apply backpressure via the socket's bufferedAmount, and failed sends cancel the caller's audio stream.
|
|
20
|
+
- e193290: Fix streaming transcription over header-capable WebSocket implementations: the realtime WebSocket handshake sent the api key in both the `openai-insecure-api-key` subprotocol and the `Authorization` header, which OpenAI rejects ("You must only send one of protocol api key and Authorization header"). The Authorization header is now stripped when the subprotocol carries the key.
|
|
21
|
+
- e193290: Strip undefined header values before the streaming transcription WebSocket constructor (header-capable implementations like `ws` throw on undefined values).
|
|
22
|
+
- Updated dependencies [e193290]
|
|
23
|
+
- @ai-sdk/provider-utils@5.0.8
|
|
24
|
+
|
|
3
25
|
## 4.0.11
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
loadApiKey,
|
|
4
4
|
loadOptionalSetting,
|
|
5
|
+
validateBaseURL,
|
|
5
6
|
withoutTrailingSlash,
|
|
6
7
|
withUserAgentSuffix
|
|
7
8
|
} from "@ai-sdk/provider-utils";
|
|
@@ -7907,16 +7908,16 @@ import {
|
|
|
7907
7908
|
convertBase64ToUint8Array as convertBase64ToUint8Array2,
|
|
7908
7909
|
convertToBase64 as convertToBase643,
|
|
7909
7910
|
createJsonResponseHandler as createJsonResponseHandler7,
|
|
7910
|
-
|
|
7911
|
+
connectToWebSocket,
|
|
7911
7912
|
mediaTypeToExtension,
|
|
7912
7913
|
parseProviderOptions as parseProviderOptions9,
|
|
7913
7914
|
postFormDataToApi as postFormDataToApi3,
|
|
7914
|
-
readWebSocketMessageText,
|
|
7915
7915
|
safeParseJSON,
|
|
7916
7916
|
serializeModelOptions as serializeModelOptions7,
|
|
7917
7917
|
toWebSocketUrl,
|
|
7918
7918
|
WORKFLOW_DESERIALIZE as WORKFLOW_DESERIALIZE7,
|
|
7919
|
-
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE7
|
|
7919
|
+
WORKFLOW_SERIALIZE as WORKFLOW_SERIALIZE7,
|
|
7920
|
+
waitForWebSocketBufferDrain
|
|
7920
7921
|
} from "@ai-sdk/provider-utils";
|
|
7921
7922
|
|
|
7922
7923
|
// src/transcription/openai-transcription-api.ts
|
|
@@ -8265,21 +8266,18 @@ function createOpenAIRealtimeTranscriptionStream({
|
|
|
8265
8266
|
};
|
|
8266
8267
|
return new ReadableStream({
|
|
8267
8268
|
start: (controller) => {
|
|
8268
|
-
const
|
|
8269
|
-
const ws = new WebSocketConstructor(
|
|
8270
|
-
url,
|
|
8271
|
-
getOpenAIRealtimeProtocols(headers),
|
|
8272
|
-
{ headers }
|
|
8273
|
-
);
|
|
8269
|
+
const realtimeConnection = getOpenAIRealtimeConnection(headers);
|
|
8274
8270
|
let audioReader;
|
|
8271
|
+
let connection;
|
|
8275
8272
|
cleanup = (closeCode) => {
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8273
|
+
if (audioReader != null) {
|
|
8274
|
+
void audioReader.cancel().catch(() => {
|
|
8275
|
+
});
|
|
8276
|
+
} else {
|
|
8277
|
+
void audio.cancel().catch(() => {
|
|
8278
|
+
});
|
|
8282
8279
|
}
|
|
8280
|
+
connection == null ? void 0 : connection.close(closeCode);
|
|
8283
8281
|
};
|
|
8284
8282
|
const finishWithError = (error) => {
|
|
8285
8283
|
if (finished) return;
|
|
@@ -8302,42 +8300,42 @@ function createOpenAIRealtimeTranscriptionStream({
|
|
|
8302
8300
|
controller.close();
|
|
8303
8301
|
cleanup(1e3);
|
|
8304
8302
|
};
|
|
8305
|
-
const
|
|
8306
|
-
var _a;
|
|
8307
|
-
finishWithError((_a = abortSignal == null ? void 0 : abortSignal.reason) != null ? _a : new Error("Aborted"));
|
|
8308
|
-
};
|
|
8309
|
-
if (abortSignal == null ? void 0 : abortSignal.aborted) {
|
|
8310
|
-
abort();
|
|
8311
|
-
return;
|
|
8312
|
-
}
|
|
8313
|
-
abortSignal == null ? void 0 : abortSignal.addEventListener("abort", abort, { once: true });
|
|
8314
|
-
const sendAudio = async () => {
|
|
8303
|
+
const sendAudio = async (socket) => {
|
|
8315
8304
|
audioReader = audio.getReader();
|
|
8316
8305
|
try {
|
|
8317
8306
|
while (true) {
|
|
8318
8307
|
const { done, value } = await audioReader.read();
|
|
8319
8308
|
if (done || finished) break;
|
|
8320
|
-
|
|
8309
|
+
socket.send(
|
|
8321
8310
|
JSON.stringify({
|
|
8322
8311
|
type: "input_audio_buffer.append",
|
|
8323
8312
|
audio: convertToBase643(value)
|
|
8324
8313
|
})
|
|
8325
8314
|
);
|
|
8315
|
+
await waitForWebSocketBufferDrain(socket);
|
|
8326
8316
|
}
|
|
8327
8317
|
} finally {
|
|
8328
8318
|
audioReader.releaseLock();
|
|
8319
|
+
audioReader = void 0;
|
|
8329
8320
|
}
|
|
8330
8321
|
if (!finished) {
|
|
8331
|
-
|
|
8322
|
+
socket.send(JSON.stringify({ type: "input_audio_buffer.commit" }));
|
|
8332
8323
|
}
|
|
8333
8324
|
};
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8325
|
+
connection = connectToWebSocket({
|
|
8326
|
+
url,
|
|
8327
|
+
protocols: realtimeConnection.protocols,
|
|
8328
|
+
headers: realtimeConnection.headers,
|
|
8329
|
+
webSocket,
|
|
8330
|
+
abortSignal,
|
|
8331
|
+
onAbort: finishWithError,
|
|
8332
|
+
onProcessingError: finishWithError,
|
|
8333
|
+
onOpen: (socket) => {
|
|
8334
|
+
controller.enqueue({ type: "stream-start", warnings });
|
|
8335
|
+
socket.send(JSON.stringify(sessionUpdate));
|
|
8336
|
+
void sendAudio(socket).catch(finishWithError);
|
|
8337
|
+
},
|
|
8338
|
+
onMessageText: async (text) => {
|
|
8341
8339
|
var _a, _b, _c, _d;
|
|
8342
8340
|
const parsed = await safeParseJSON({ text });
|
|
8343
8341
|
if (!parsed.success) return;
|
|
@@ -8365,17 +8363,17 @@ function createOpenAIRealtimeTranscriptionStream({
|
|
|
8365
8363
|
break;
|
|
8366
8364
|
}
|
|
8367
8365
|
}
|
|
8368
|
-
}
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
};
|
|
8366
|
+
},
|
|
8367
|
+
onSocketError: () => {
|
|
8368
|
+
finishWithError(new Error("OpenAI realtime transcription error"));
|
|
8369
|
+
},
|
|
8370
|
+
onClose: () => {
|
|
8371
|
+
if (finished) return;
|
|
8372
|
+
finished = true;
|
|
8373
|
+
cleanup();
|
|
8374
|
+
controller.close();
|
|
8375
|
+
}
|
|
8376
|
+
});
|
|
8379
8377
|
},
|
|
8380
8378
|
cancel: () => {
|
|
8381
8379
|
if (finished) return;
|
|
@@ -8412,11 +8410,26 @@ function buildOpenAIRealtimeTranscriptionSession({
|
|
|
8412
8410
|
}
|
|
8413
8411
|
};
|
|
8414
8412
|
}
|
|
8415
|
-
function
|
|
8413
|
+
function getOpenAIRealtimeConnection(headers) {
|
|
8416
8414
|
var _a;
|
|
8417
|
-
|
|
8418
|
-
|
|
8419
|
-
|
|
8415
|
+
let authorization;
|
|
8416
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
8417
|
+
if (key.toLowerCase() === "authorization" && value != null) {
|
|
8418
|
+
authorization = value;
|
|
8419
|
+
}
|
|
8420
|
+
}
|
|
8421
|
+
const token = (_a = authorization == null ? void 0 : authorization.match(/^bearer\s+(.+)$/i)) == null ? void 0 : _a[1];
|
|
8422
|
+
if (token == null) {
|
|
8423
|
+
return { protocols: ["realtime"], headers };
|
|
8424
|
+
}
|
|
8425
|
+
return {
|
|
8426
|
+
protocols: ["realtime", `openai-insecure-api-key.${token}`],
|
|
8427
|
+
headers: Object.fromEntries(
|
|
8428
|
+
Object.entries(headers).filter(
|
|
8429
|
+
([key]) => key.toLowerCase() !== "authorization"
|
|
8430
|
+
)
|
|
8431
|
+
)
|
|
8432
|
+
};
|
|
8420
8433
|
}
|
|
8421
8434
|
|
|
8422
8435
|
// src/skills/openai-skills.ts
|
|
@@ -8504,16 +8517,18 @@ var OpenAISkills = class {
|
|
|
8504
8517
|
};
|
|
8505
8518
|
|
|
8506
8519
|
// src/version.ts
|
|
8507
|
-
var VERSION = true ? "4.0.
|
|
8520
|
+
var VERSION = true ? "4.0.13" : "0.0.0-test";
|
|
8508
8521
|
|
|
8509
8522
|
// src/openai-provider.ts
|
|
8510
8523
|
function createOpenAI(options = {}) {
|
|
8511
8524
|
var _a, _b;
|
|
8512
8525
|
const baseURL = (_a = withoutTrailingSlash(
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8526
|
+
validateBaseURL(
|
|
8527
|
+
loadOptionalSetting({
|
|
8528
|
+
settingValue: options.baseURL,
|
|
8529
|
+
environmentVariableName: "OPENAI_BASE_URL"
|
|
8530
|
+
})
|
|
8531
|
+
)
|
|
8517
8532
|
)) != null ? _a : "https://api.openai.com/v1";
|
|
8518
8533
|
const providerName = (_b = options.name) != null ? _b : "openai";
|
|
8519
8534
|
const getHeaders = () => withUserAgentSuffix(
|