🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@ai-sdk/openai

Package Overview
Dependencies
Maintainers
3
Versions
617
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ai-sdk/openai - npm Package Compare versions

Comparing version
4.0.11
to
4.0.13
+4
-4
package.json
{
"name": "@ai-sdk/openai",
"version": "4.0.11",
"version": "4.0.13",
"type": "module",

@@ -39,3 +39,3 @@ "license": "Apache-2.0",

"@ai-sdk/provider": "4.0.3",
"@ai-sdk/provider-utils": "5.0.7"
"@ai-sdk/provider-utils": "5.0.9"
},

@@ -47,4 +47,4 @@ "devDependencies": {

"zod": "3.25.76",
"@ai-sdk/test-server": "2.0.0",
"@vercel/ai-tsconfig": "0.0.0"
"@vercel/ai-tsconfig": "0.0.0",
"@ai-sdk/test-server": "2.0.0"
},

@@ -51,0 +51,0 @@ "peerDependencies": {

@@ -16,2 +16,3 @@ import type {

loadOptionalSetting,
validateBaseURL,
withoutTrailingSlash,

@@ -179,6 +180,8 @@ withUserAgentSuffix,

withoutTrailingSlash(
loadOptionalSetting({
settingValue: options.baseURL,
environmentVariableName: 'OPENAI_BASE_URL',
}),
validateBaseURL(
loadOptionalSetting({
settingValue: options.baseURL,
environmentVariableName: 'OPENAI_BASE_URL',
}),
),
) ?? 'https://api.openai.com/v1';

@@ -185,0 +188,0 @@

@@ -13,7 +13,6 @@ import {

createJsonResponseHandler,
getWebSocketConstructor,
connectToWebSocket,
mediaTypeToExtension,
parseProviderOptions,
postFormDataToApi,
readWebSocketMessageText,
safeParseJSON,

@@ -24,2 +23,5 @@ serializeModelOptions,

WORKFLOW_SERIALIZE,
waitForWebSocketBufferDrain,
type WebSocketConnection,
type WebSocketLike,
} from '@ai-sdk/provider-utils';

@@ -395,18 +397,17 @@ import type { OpenAIConfig } from '../openai-config';

start: controller => {
const WebSocketConstructor = getWebSocketConstructor(webSocket);
const ws = new WebSocketConstructor(
url,
getOpenAIRealtimeProtocols(headers),
{ headers },
);
const realtimeConnection = getOpenAIRealtimeConnection(headers);
let audioReader:
| ReadableStreamDefaultReader<Uint8Array | string>
| undefined;
let connection: WebSocketConnection | undefined;
cleanup = (closeCode?: number) => {
abortSignal?.removeEventListener('abort', abort);
void audioReader?.cancel().catch(() => {});
try {
ws.close(closeCode);
} catch {}
if (audioReader != null) {
void audioReader.cancel().catch(() => {});
} else {
// pre-open failure or abort: cancel the caller's audio stream so an
// upstream producer piping into it does not hang:
void audio.cancel().catch(() => {});
}
connection?.close(closeCode);
};

@@ -437,12 +438,3 @@

const abort = () => {
finishWithError(abortSignal?.reason ?? new Error('Aborted'));
};
if (abortSignal?.aborted) {
abort();
return;
}
abortSignal?.addEventListener('abort', abort, { once: true });
const sendAudio = async () => {
const sendAudio = async (socket: WebSocketLike) => {
audioReader = audio.getReader();

@@ -453,3 +445,3 @@ try {

if (done || finished) break;
ws.send(
socket.send(
JSON.stringify({

@@ -460,64 +452,70 @@ type: 'input_audio_buffer.append',

);
// backpressure: pause reads while the socket buffer is full
await waitForWebSocketBufferDrain(socket);
}
} finally {
audioReader.releaseLock();
// unlocked again: cleanup must cancel `audio`, not the reader
audioReader = undefined;
}
if (!finished) {
ws.send(JSON.stringify({ type: 'input_audio_buffer.commit' }));
socket.send(JSON.stringify({ type: 'input_audio_buffer.commit' }));
}
};
ws.onopen = () => {
controller.enqueue({ type: 'stream-start', warnings });
ws.send(JSON.stringify(sessionUpdate));
void sendAudio().catch(finishWithError);
};
connection = connectToWebSocket({
url,
protocols: realtimeConnection.protocols,
headers: realtimeConnection.headers,
webSocket,
abortSignal,
onAbort: finishWithError,
onProcessingError: finishWithError,
onOpen: socket => {
controller.enqueue({ type: 'stream-start', warnings });
socket.send(JSON.stringify(sessionUpdate));
void sendAudio(socket).catch(finishWithError);
},
onMessageText: async text => {
const parsed = await safeParseJSON({ text });
if (!parsed.success) return;
const raw = parsed.value as OpenAIRealtimeTranscriptionEvent;
ws.onmessage = event => {
void readWebSocketMessageText(event.data)
.then(async text => {
const parsed = await safeParseJSON({ text });
if (!parsed.success) return;
const raw = parsed.value as OpenAIRealtimeTranscriptionEvent;
if (includeRawChunks) {
controller.enqueue({ type: 'raw', rawValue: raw });
}
if (includeRawChunks) {
controller.enqueue({ type: 'raw', rawValue: raw });
switch (raw.type) {
case 'conversation.item.input_audio_transcription.delta': {
controller.enqueue({
type: 'transcript-delta',
id: raw.item_id,
delta: raw.delta ?? '',
});
break;
}
switch (raw.type) {
case 'conversation.item.input_audio_transcription.delta': {
controller.enqueue({
type: 'transcript-delta',
id: raw.item_id,
delta: raw.delta ?? '',
});
break;
}
case 'conversation.item.input_audio_transcription.completed': {
finish(raw.transcript ?? '', raw.item_id);
break;
}
case 'conversation.item.input_audio_transcription.completed': {
finish(raw.transcript ?? '', raw.item_id);
break;
}
case 'error': {
finishWithError(
new Error(raw.error?.message ?? 'OpenAI realtime error'),
);
break;
}
case 'error': {
finishWithError(
new Error(raw.error?.message ?? 'OpenAI realtime error'),
);
break;
}
})
.catch(finishWithError);
};
ws.onerror = () => {
finishWithError(new Error('OpenAI realtime transcription error'));
};
ws.onclose = () => {
if (finished) return;
finished = true;
cleanup();
controller.close();
};
}
},
onSocketError: () => {
finishWithError(new Error('OpenAI realtime transcription error'));
},
onClose: () => {
if (finished) return;
finished = true;
cleanup();
controller.close();
},
});
},

@@ -573,13 +571,34 @@

function getOpenAIRealtimeProtocols(
// The bearer token rides the `openai-insecure-api-key` subprotocol (native
// `WebSocket` cannot send headers) and the Authorization header is stripped:
// OpenAI rejects handshakes that send both auth channels.
function getOpenAIRealtimeConnection(
headers: Record<string, string | undefined>,
): string[] {
const authorization = headers.Authorization ?? headers.authorization;
const token = authorization?.startsWith('Bearer ')
? authorization.slice('Bearer '.length)
: undefined;
): {
protocols: string[];
headers: Record<string, string | undefined>;
} {
// last case-variant wins: combineHeaders keeps case-distinct keys and
// spreads per-call headers after configuration headers
let authorization: string | undefined;
for (const [key, value] of Object.entries(headers)) {
if (key.toLowerCase() === 'authorization' && value != null) {
authorization = value;
}
}
// the HTTP auth scheme is case-insensitive
const token = authorization?.match(/^bearer\s+(.+)$/i)?.[1];
return token == null
? ['realtime']
: ['realtime', `openai-insecure-api-key.${token}`];
if (token == null) {
return { protocols: ['realtime'], headers };
}
return {
protocols: ['realtime', `openai-insecure-api-key.${token}`],
headers: Object.fromEntries(
Object.entries(headers).filter(
([key]) => key.toLowerCase() !== 'authorization',
),
),
};
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display