Skip to content

Commit ff7cdff

Browse files
committed
address feedback
1 parent 390c131 commit ff7cdff

File tree

2 files changed

+32
-24
lines changed

2 files changed

+32
-24
lines changed

src/beta/realtime/websocket.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ export class OpenAIRealtimeWebSocket extends OpenAIRealtimeEmitter {
3131
* @internal
3232
*/
3333
onURL?: (url: URL) => void;
34+
/** Indicates the token was resolved by the factory just before connecting. @internal */
35+
__resolvedApiKey?: boolean;
3436
},
3537
client?: Pick<OpenAI, 'apiKey' | 'baseURL'>,
3638
) {
3739
super();
38-
40+
const hasProvider = typeof (client as any)?._options?.apiKey === 'function';
3941
const dangerouslyAllowBrowser =
4042
props.dangerouslyAllowBrowser ??
4143
(client as any)?._options?.dangerouslyAllowBrowser ??
42-
(typeof (client as any)?._options?.apiKey === 'string' && client?.apiKey?.startsWith('ek_') ?
43-
true
44-
: null);
44+
(client?.apiKey?.startsWith('ek_') ? true : null);
4545
if (!dangerouslyAllowBrowser && isRunningInBrowser()) {
4646
throw new OpenAIError(
4747
"It looks like you're running in a browser-like environment.\n\nThis is disabled by default, as it risks exposing your secret API credentials to attackers.\n\nYou can avoid this error by creating an ephemeral session token:\nhttps://platform.openai.com/docs/api-reference/realtime-sessions\n",
@@ -50,7 +50,7 @@ export class OpenAIRealtimeWebSocket extends OpenAIRealtimeEmitter {
5050

5151
client ??= new OpenAI({ dangerouslyAllowBrowser });
5252

53-
if (typeof (client as any)?._options?.apiKey !== 'string') {
53+
if (hasProvider && !props?.__resolvedApiKey) {
5454
throw new Error(
5555
[
5656
'Cannot open Realtime WebSocket with a function-based apiKey.',
@@ -109,8 +109,7 @@ export class OpenAIRealtimeWebSocket extends OpenAIRealtimeEmitter {
109109
client: Pick<OpenAI, 'apiKey' | 'baseURL' | '_callApiKey'>,
110110
props: { model: string; dangerouslyAllowBrowser?: boolean },
111111
): Promise<OpenAIRealtimeWebSocket> {
112-
await client._callApiKey();
113-
return new OpenAIRealtimeWebSocket(props, client);
112+
return new OpenAIRealtimeWebSocket({ ...props, __resolvedApiKey: await client._callApiKey() }, client);
114113
}
115114

116115
static async azure(
@@ -135,6 +134,7 @@ export class OpenAIRealtimeWebSocket extends OpenAIRealtimeEmitter {
135134
model: deploymentName,
136135
onURL,
137136
...(dangerouslyAllowBrowser ? { dangerouslyAllowBrowser } : {}),
137+
__resolvedApiKey: isToken,
138138
},
139139
client,
140140
);

src/beta/realtime/ws.ts

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,21 @@ export class OpenAIRealtimeWS extends OpenAIRealtimeEmitter {
88
socket: WS.WebSocket;
99

1010
constructor(
11-
props: { model: string; options?: WS.ClientOptions | undefined },
11+
props: {
12+
model: string;
13+
options?: WS.ClientOptions | undefined;
14+
/** @internal */ __resolvedApiKey?: boolean;
15+
},
1216
client?: Pick<OpenAI, 'apiKey' | 'baseURL'>,
1317
) {
1418
super();
1519
client ??= new OpenAI();
16-
if (typeof (client as any)._options.apiKey !== 'string') {
20+
const hasProvider = typeof (client as any)?._options?.apiKey === 'function';
21+
if (hasProvider && !props.__resolvedApiKey) {
1722
throw new Error(
1823
[
1924
'Cannot open Realtime WebSocket with a function-based apiKey.',
20-
'Use the factory so the key is resolved before connecting:',
25+
'Use the factory so the key is resolved just before connecting:',
2126
'- OpenAIRealtimeWS.create(client, { model })',
2227
].join('\n'),
2328
);
@@ -27,7 +32,7 @@ export class OpenAIRealtimeWS extends OpenAIRealtimeEmitter {
2732
...props.options,
2833
headers: {
2934
...props.options?.headers,
30-
...(isAzure(client) ? {} : { Authorization: `Bearer ${client.apiKey}` }),
35+
...(isAzure(client) && !props.__resolvedApiKey ? {} : { Authorization: `Bearer ${client.apiKey}` }),
3136
'OpenAI-Beta': 'realtime=v1',
3237
},
3338
});
@@ -63,20 +68,30 @@ export class OpenAIRealtimeWS extends OpenAIRealtimeEmitter {
6368
client: Pick<OpenAI, 'apiKey' | 'baseURL' | '_callApiKey'>,
6469
props: { model: string; options?: WS.ClientOptions | undefined },
6570
): Promise<OpenAIRealtimeWS> {
66-
await client._callApiKey();
67-
return new OpenAIRealtimeWS(props, client);
71+
return new OpenAIRealtimeWS({ ...props, __resolvedApiKey: await client._callApiKey() }, client);
6872
}
6973

7074
static async azure(
7175
client: Pick<AzureOpenAI, '_callApiKey' | 'apiVersion' | 'apiKey' | 'baseURL' | 'deploymentName'>,
72-
options: { deploymentName?: string; options?: WS.ClientOptions | undefined } = {},
76+
props: { deploymentName?: string; options?: WS.ClientOptions | undefined } = {},
7377
): Promise<OpenAIRealtimeWS> {
74-
const deploymentName = options.deploymentName ?? client.deploymentName;
78+
const isToken = await client._callApiKey();
79+
const deploymentName = props.deploymentName ?? client.deploymentName;
7580
if (!deploymentName) {
7681
throw new Error('No deployment name provided');
7782
}
7883
return new OpenAIRealtimeWS(
79-
{ model: deploymentName, options: { headers: await getAzureHeaders(client) } },
84+
{
85+
model: deploymentName,
86+
options: {
87+
...props.options,
88+
headers: {
89+
...props.options?.headers,
90+
...(isToken ? {} : { 'api-key': client.apiKey }),
91+
},
92+
},
93+
__resolvedApiKey: isToken,
94+
},
8095
client,
8196
);
8297
}
@@ -98,11 +113,4 @@ export class OpenAIRealtimeWS extends OpenAIRealtimeEmitter {
98113
}
99114
}
100115

101-
async function getAzureHeaders(client: Pick<AzureOpenAI, '_callApiKey' | 'apiKey'>) {
102-
const isToken = await client._callApiKey();
103-
if (isToken) {
104-
return { Authorization: `Bearer ${isToken}` };
105-
} else {
106-
return { 'api-key': client.apiKey };
107-
}
108-
}
116+
// getAzureHeaders inlined into azure()

0 commit comments

Comments
 (0)