openai 7.24.0 → 7.26.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/CHANGELOG.md +38 -0
- package/client.d.mts +5 -6
- package/client.d.mts.map +1 -1
- package/client.d.ts +5 -6
- package/client.d.ts.map +1 -1
- package/client.js +38 -10
- package/client.js.map +1 -1
- package/client.mjs +39 -11
- package/client.mjs.map +1 -1
- package/core/streaming.d.mts +4 -0
- package/core/streaming.d.mts.map +1 -1
- package/core/streaming.d.ts +4 -0
- package/core/streaming.d.ts.map +1 -1
- package/core/streaming.js +6 -0
- package/core/streaming.js.map +1 -1
- package/core/streaming.mjs +6 -0
- package/core/streaming.mjs.map +1 -1
- package/helpers/beta/agents/standard-schema.d.mts +7 -0
- package/helpers/beta/agents/standard-schema.d.mts.map +1 -0
- package/helpers/beta/agents/standard-schema.d.ts +7 -0
- package/helpers/beta/agents/standard-schema.d.ts.map +1 -0
- package/helpers/beta/agents/standard-schema.js +12 -0
- package/helpers/beta/agents/standard-schema.js.map +1 -0
- package/helpers/beta/agents/standard-schema.mjs +9 -0
- package/helpers/beta/agents/standard-schema.mjs.map +1 -0
- package/helpers/beta/agents/zod.d.mts +10 -0
- package/helpers/beta/agents/zod.d.mts.map +1 -0
- package/helpers/beta/agents/zod.d.ts +10 -0
- package/helpers/beta/agents/zod.d.ts.map +1 -0
- package/helpers/beta/agents/zod.js +12 -0
- package/helpers/beta/agents/zod.js.map +1 -0
- package/helpers/beta/agents/zod.mjs +9 -0
- package/helpers/beta/agents/zod.mjs.map +1 -0
- package/internal/realtime-credentials.d.mts +22 -1
- package/internal/realtime-credentials.d.mts.map +1 -1
- package/internal/realtime-credentials.d.ts +22 -1
- package/internal/realtime-credentials.d.ts.map +1 -1
- package/internal/realtime-credentials.js +48 -4
- package/internal/realtime-credentials.js.map +1 -1
- package/internal/realtime-credentials.mjs +45 -4
- package/internal/realtime-credentials.mjs.map +1 -1
- package/internal/responses-ws-credentials.d.mts +23 -0
- package/internal/responses-ws-credentials.d.mts.map +1 -0
- package/internal/responses-ws-credentials.d.ts +23 -0
- package/internal/responses-ws-credentials.d.ts.map +1 -0
- package/internal/responses-ws-credentials.js +108 -0
- package/internal/responses-ws-credentials.js.map +1 -0
- package/internal/responses-ws-credentials.mjs +104 -0
- package/internal/responses-ws-credentials.mjs.map +1 -0
- package/internal/ws-adapter-node.d.mts +4 -0
- package/internal/ws-adapter-node.d.mts.map +1 -1
- package/internal/ws-adapter-node.d.ts +4 -0
- package/internal/ws-adapter-node.d.ts.map +1 -1
- package/internal/ws-adapter-node.js +56 -0
- package/internal/ws-adapter-node.js.map +1 -1
- package/internal/ws-adapter-node.mjs +55 -0
- package/internal/ws-adapter-node.mjs.map +1 -1
- package/internal/ws.d.mts +27 -4
- package/internal/ws.d.mts.map +1 -1
- package/internal/ws.d.ts +27 -4
- package/internal/ws.d.ts.map +1 -1
- package/internal/ws.js +105 -5
- package/internal/ws.js.map +1 -1
- package/internal/ws.mjs +102 -4
- package/internal/ws.mjs.map +1 -1
- package/lib/agents/agent-session-stream.d.mts +13 -4
- package/lib/agents/agent-session-stream.d.mts.map +1 -1
- package/lib/agents/agent-session-stream.d.ts +13 -4
- package/lib/agents/agent-session-stream.d.ts.map +1 -1
- package/lib/agents/agent-session-stream.js +24 -21
- package/lib/agents/agent-session-stream.js.map +1 -1
- package/lib/agents/agent-session-stream.mjs +24 -21
- package/lib/agents/agent-session-stream.mjs.map +1 -1
- package/lib/beta/agents/agent-session-create-stream.d.mts +15 -0
- package/lib/beta/agents/agent-session-create-stream.d.mts.map +1 -0
- package/lib/beta/agents/agent-session-create-stream.d.ts +15 -0
- package/lib/beta/agents/agent-session-create-stream.d.ts.map +1 -0
- package/lib/beta/agents/agent-session-create-stream.js +25 -0
- package/lib/beta/agents/agent-session-create-stream.js.map +1 -0
- package/lib/beta/agents/agent-session-create-stream.mjs +22 -0
- package/lib/beta/agents/agent-session-create-stream.mjs.map +1 -0
- package/lib/beta/agents/agent-turn-result-collector.d.mts +17 -0
- package/lib/beta/agents/agent-turn-result-collector.d.mts.map +1 -0
- package/lib/beta/agents/agent-turn-result-collector.d.ts +17 -0
- package/lib/beta/agents/agent-turn-result-collector.d.ts.map +1 -0
- package/lib/beta/agents/agent-turn-result-collector.js +99 -0
- package/lib/beta/agents/agent-turn-result-collector.js.map +1 -0
- package/lib/beta/agents/agent-turn-result-collector.mjs +95 -0
- package/lib/beta/agents/agent-turn-result-collector.mjs.map +1 -0
- package/lib/beta/agents/agent-turn-result-error.d.mts +16 -0
- package/lib/beta/agents/agent-turn-result-error.d.mts.map +1 -0
- package/lib/beta/agents/agent-turn-result-error.d.ts +16 -0
- package/lib/beta/agents/agent-turn-result-error.d.ts.map +1 -0
- package/lib/beta/agents/agent-turn-result-error.js +22 -0
- package/lib/beta/agents/agent-turn-result-error.js.map +1 -0
- package/lib/beta/agents/agent-turn-result-error.mjs +18 -0
- package/lib/beta/agents/agent-turn-result-error.mjs.map +1 -0
- package/lib/beta/agents/agent-turn-result.d.mts +12 -0
- package/lib/beta/agents/agent-turn-result.d.mts.map +1 -0
- package/lib/beta/agents/agent-turn-result.d.ts +12 -0
- package/lib/beta/agents/agent-turn-result.d.ts.map +1 -0
- package/lib/beta/agents/agent-turn-result.js +22 -0
- package/lib/beta/agents/agent-turn-result.js.map +1 -0
- package/lib/beta/agents/agent-turn-result.mjs +18 -0
- package/lib/beta/agents/agent-turn-result.mjs.map +1 -0
- package/lib/beta/agents/function-tool.d.mts +27 -0
- package/lib/beta/agents/function-tool.d.mts.map +1 -0
- package/lib/beta/agents/function-tool.d.ts +27 -0
- package/lib/beta/agents/function-tool.d.ts.map +1 -0
- package/lib/beta/agents/function-tool.js +48 -0
- package/lib/beta/agents/function-tool.js.map +1 -0
- package/lib/beta/agents/function-tool.mjs +45 -0
- package/lib/beta/agents/function-tool.mjs.map +1 -0
- package/lib/beta/agents/output-format-types.d.mts +11 -0
- package/lib/beta/agents/output-format-types.d.mts.map +1 -0
- package/lib/beta/agents/output-format-types.d.ts +11 -0
- package/lib/beta/agents/output-format-types.d.ts.map +1 -0
- package/lib/beta/agents/output-format-types.js +3 -0
- package/lib/beta/agents/output-format-types.js.map +1 -0
- package/lib/beta/agents/output-format-types.mjs +2 -0
- package/lib/beta/agents/output-format-types.mjs.map +1 -0
- package/lib/beta/agents/output-format.d.mts +8 -0
- package/lib/beta/agents/output-format.d.mts.map +1 -0
- package/lib/beta/agents/output-format.d.ts +8 -0
- package/lib/beta/agents/output-format.d.ts.map +1 -0
- package/lib/beta/agents/output-format.js +20 -0
- package/lib/beta/agents/output-format.js.map +1 -0
- package/lib/beta/agents/output-format.mjs +14 -0
- package/lib/beta/agents/output-format.mjs.map +1 -0
- package/lib/beta/agents/output-parse-error.d.mts +11 -0
- package/lib/beta/agents/output-parse-error.d.mts.map +1 -0
- package/lib/beta/agents/output-parse-error.d.ts +11 -0
- package/lib/beta/agents/output-parse-error.d.ts.map +1 -0
- package/lib/beta/agents/output-parse-error.js +22 -0
- package/lib/beta/agents/output-parse-error.js.map +1 -0
- package/lib/beta/agents/output-parse-error.mjs +18 -0
- package/lib/beta/agents/output-parse-error.mjs.map +1 -0
- package/lib/beta/agents/parse-result.d.mts +95 -0
- package/lib/beta/agents/parse-result.d.mts.map +1 -0
- package/lib/beta/agents/parse-result.d.ts +95 -0
- package/lib/beta/agents/parse-result.d.ts.map +1 -0
- package/lib/beta/agents/parse-result.js +116 -0
- package/lib/beta/agents/parse-result.js.map +1 -0
- package/lib/beta/agents/parse-result.mjs +110 -0
- package/lib/beta/agents/parse-result.mjs.map +1 -0
- package/lib/beta/agents/parsed-agent-turn-result.d.mts +8 -0
- package/lib/beta/agents/parsed-agent-turn-result.d.mts.map +1 -0
- package/lib/beta/agents/parsed-agent-turn-result.d.ts +8 -0
- package/lib/beta/agents/parsed-agent-turn-result.d.ts.map +1 -0
- package/lib/beta/agents/parsed-agent-turn-result.js +14 -0
- package/lib/beta/agents/parsed-agent-turn-result.js.map +1 -0
- package/lib/beta/agents/parsed-agent-turn-result.mjs +10 -0
- package/lib/beta/agents/parsed-agent-turn-result.mjs.map +1 -0
- package/lib/beta/agents/result-collection.d.mts +15 -0
- package/lib/beta/agents/result-collection.d.mts.map +1 -0
- package/lib/beta/agents/result-collection.d.ts +15 -0
- package/lib/beta/agents/result-collection.d.ts.map +1 -0
- package/lib/beta/agents/result-collection.js +110 -0
- package/lib/beta/agents/result-collection.js.map +1 -0
- package/lib/beta/agents/result-collection.mjs +106 -0
- package/lib/beta/agents/result-collection.mjs.map +1 -0
- package/lib/beta/agents/tool-output.d.mts +7 -0
- package/lib/beta/agents/tool-output.d.mts.map +1 -0
- package/lib/beta/agents/tool-output.d.ts +7 -0
- package/lib/beta/agents/tool-output.d.ts.map +1 -0
- package/lib/beta/agents/tool-output.js +26 -0
- package/lib/beta/agents/tool-output.js.map +1 -0
- package/lib/beta/agents/tool-output.mjs +23 -0
- package/lib/beta/agents/tool-output.mjs.map +1 -0
- package/lib/responses/responses-websocket-session.d.mts +1 -1
- package/lib/responses/responses-websocket-session.d.mts.map +1 -1
- package/lib/responses/responses-websocket-session.d.ts +1 -1
- package/lib/responses/responses-websocket-session.d.ts.map +1 -1
- package/lib/responses/responses-websocket-session.js +12 -2
- package/lib/responses/responses-websocket-session.js.map +1 -1
- package/lib/responses/responses-websocket-session.mjs +12 -2
- package/lib/responses/responses-websocket-session.mjs.map +1 -1
- package/package.json +1 -1
- package/resources/audio/speech.d.mts +1 -1
- package/resources/audio/speech.d.ts +1 -1
- package/resources/audio/speech.js +1 -1
- package/resources/audio/speech.mjs +1 -1
- package/resources/beta/agents/agents.d.mts +574 -11
- package/resources/beta/agents/agents.d.mts.map +1 -1
- package/resources/beta/agents/agents.d.ts +574 -11
- package/resources/beta/agents/agents.d.ts.map +1 -1
- package/resources/beta/agents/agents.js.map +1 -1
- package/resources/beta/agents/agents.mjs.map +1 -1
- package/resources/beta/agents/environments/templates.d.mts +56 -3
- package/resources/beta/agents/environments/templates.d.mts.map +1 -1
- package/resources/beta/agents/environments/templates.d.ts +56 -3
- package/resources/beta/agents/environments/templates.d.ts.map +1 -1
- package/resources/beta/agents/index.d.mts +1 -1
- package/resources/beta/agents/index.d.mts.map +1 -1
- package/resources/beta/agents/index.d.ts +1 -1
- package/resources/beta/agents/index.d.ts.map +1 -1
- package/resources/beta/agents/index.js.map +1 -1
- package/resources/beta/agents/index.mjs.map +1 -1
- package/resources/beta/agents/sessions/events.d.mts +16 -12
- package/resources/beta/agents/sessions/events.d.mts.map +1 -1
- package/resources/beta/agents/sessions/events.d.ts +16 -12
- package/resources/beta/agents/sessions/events.d.ts.map +1 -1
- package/resources/beta/agents/sessions/events.js +16 -12
- package/resources/beta/agents/sessions/events.js.map +1 -1
- package/resources/beta/agents/sessions/events.mjs +16 -12
- package/resources/beta/agents/sessions/events.mjs.map +1 -1
- package/resources/beta/agents/sessions/index.d.mts +1 -0
- package/resources/beta/agents/sessions/index.d.mts.map +1 -1
- package/resources/beta/agents/sessions/index.d.ts +1 -0
- package/resources/beta/agents/sessions/index.d.ts.map +1 -1
- package/resources/beta/agents/sessions/index.js +3 -1
- package/resources/beta/agents/sessions/index.js.map +1 -1
- package/resources/beta/agents/sessions/index.mjs +1 -0
- package/resources/beta/agents/sessions/index.mjs.map +1 -1
- package/resources/beta/agents/sessions/sessions.d.mts +16 -4
- package/resources/beta/agents/sessions/sessions.d.mts.map +1 -1
- package/resources/beta/agents/sessions/sessions.d.ts +16 -4
- package/resources/beta/agents/sessions/sessions.d.ts.map +1 -1
- package/resources/beta/agents/sessions/sessions.js +17 -4
- package/resources/beta/agents/sessions/sessions.js.map +1 -1
- package/resources/beta/agents/sessions/sessions.mjs +17 -4
- package/resources/beta/agents/sessions/sessions.mjs.map +1 -1
- package/resources/beta/agents/sessions/traces.d.mts +68 -0
- package/resources/beta/agents/sessions/traces.d.mts.map +1 -0
- package/resources/beta/agents/sessions/traces.d.ts +68 -0
- package/resources/beta/agents/sessions/traces.d.ts.map +1 -0
- package/resources/beta/agents/sessions/traces.js +81 -0
- package/resources/beta/agents/sessions/traces.js.map +1 -0
- package/resources/beta/agents/sessions/traces.mjs +77 -0
- package/resources/beta/agents/sessions/traces.mjs.map +1 -0
- package/resources/beta/beta.d.mts +2 -2
- package/resources/beta/beta.d.mts.map +1 -1
- package/resources/beta/beta.d.ts +2 -2
- package/resources/beta/beta.d.ts.map +1 -1
- package/resources/beta/beta.js.map +1 -1
- package/resources/beta/beta.mjs.map +1 -1
- package/resources/beta/index.d.mts +1 -1
- package/resources/beta/index.d.mts.map +1 -1
- package/resources/beta/index.d.ts +1 -1
- package/resources/beta/index.d.ts.map +1 -1
- package/resources/beta/index.js.map +1 -1
- package/resources/beta/index.mjs.map +1 -1
- package/resources/beta/responses/ws-base.d.mts +3 -1
- package/resources/beta/responses/ws-base.d.mts.map +1 -1
- package/resources/beta/responses/ws-base.d.ts +3 -1
- package/resources/beta/responses/ws-base.d.ts.map +1 -1
- package/resources/beta/responses/ws-base.js +35 -6
- package/resources/beta/responses/ws-base.js.map +1 -1
- package/resources/beta/responses/ws-base.mjs +35 -6
- package/resources/beta/responses/ws-base.mjs.map +1 -1
- package/resources/beta/responses/ws.d.mts +10 -0
- package/resources/beta/responses/ws.d.mts.map +1 -1
- package/resources/beta/responses/ws.d.ts +10 -0
- package/resources/beta/responses/ws.d.ts.map +1 -1
- package/resources/beta/responses/ws.js +23 -22
- package/resources/beta/responses/ws.js.map +1 -1
- package/resources/beta/responses/ws.mjs +24 -23
- package/resources/beta/responses/ws.mjs.map +1 -1
- package/resources/chat/completions/completions.d.mts +1 -1
- package/resources/chat/completions/completions.d.mts.map +1 -1
- package/resources/chat/completions/completions.d.ts +1 -1
- package/resources/chat/completions/completions.d.ts.map +1 -1
- package/resources/evals/runs/runs.js +1 -1
- package/resources/evals/runs/runs.js.map +1 -1
- package/resources/evals/runs/runs.mjs +1 -1
- package/resources/evals/runs/runs.mjs.map +1 -1
- package/resources/live/forks/forks.d.mts +5 -1
- package/resources/live/forks/forks.d.mts.map +1 -1
- package/resources/live/forks/forks.d.ts +5 -1
- package/resources/live/forks/forks.d.ts.map +1 -1
- package/resources/live/live.d.mts +5 -1
- package/resources/live/live.d.mts.map +1 -1
- package/resources/live/live.d.ts +5 -1
- package/resources/live/live.d.ts.map +1 -1
- package/resources/live/live.js.map +1 -1
- package/resources/live/live.mjs.map +1 -1
- package/resources/responses/ws-base.d.mts +3 -1
- package/resources/responses/ws-base.d.mts.map +1 -1
- package/resources/responses/ws-base.d.ts +3 -1
- package/resources/responses/ws-base.d.ts.map +1 -1
- package/resources/responses/ws-base.js +35 -6
- package/resources/responses/ws-base.js.map +1 -1
- package/resources/responses/ws-base.mjs +35 -6
- package/resources/responses/ws-base.mjs.map +1 -1
- package/resources/responses/ws.d.mts +10 -0
- package/resources/responses/ws.d.mts.map +1 -1
- package/resources/responses/ws.d.ts +10 -0
- package/resources/responses/ws.d.ts.map +1 -1
- package/resources/responses/ws.js +23 -22
- package/resources/responses/ws.js.map +1 -1
- package/resources/responses/ws.mjs +24 -23
- package/resources/responses/ws.mjs.map +1 -1
- package/src/client.ts +55 -19
- package/src/core/streaming.ts +9 -0
- package/src/helpers/beta/agents/standard-schema.ts +13 -0
- package/src/helpers/beta/agents/zod.ts +10 -0
- package/src/internal/realtime-credentials.ts +70 -4
- package/src/internal/responses-ws-credentials.ts +160 -0
- package/src/internal/ws-adapter-node.ts +66 -0
- package/src/internal/ws.ts +139 -11
- package/src/lib/agents/agent-session-stream.ts +36 -24
- package/src/lib/beta/agents/agent-session-create-stream.ts +36 -0
- package/src/lib/beta/agents/agent-turn-result-collector.ts +122 -0
- package/src/lib/beta/agents/agent-turn-result-error.ts +33 -0
- package/src/lib/beta/agents/agent-turn-result.ts +22 -0
- package/src/lib/beta/agents/function-tool.ts +60 -0
- package/src/lib/beta/agents/output-format-types.ts +12 -0
- package/src/lib/beta/agents/output-format.ts +20 -0
- package/src/lib/beta/agents/output-parse-error.ts +16 -0
- package/src/lib/beta/agents/parse-result.ts +133 -0
- package/src/lib/beta/agents/parsed-agent-turn-result.ts +12 -0
- package/src/lib/beta/agents/result-collection.ts +115 -0
- package/src/lib/beta/agents/tool-output.ts +22 -0
- package/src/lib/responses/responses-websocket-session.ts +14 -3
- package/src/resources/audio/speech.ts +1 -1
- package/src/resources/beta/agents/agents.ts +697 -6
- package/src/resources/beta/agents/environments/templates.ts +64 -3
- package/src/resources/beta/agents/index.ts +3 -0
- package/src/resources/beta/agents/sessions/events.ts +16 -12
- package/src/resources/beta/agents/sessions/index.ts +1 -0
- package/src/resources/beta/agents/sessions/sessions.ts +48 -18
- package/src/resources/beta/agents/sessions/traces.ts +298 -0
- package/src/resources/beta/beta.ts +6 -0
- package/src/resources/beta/index.ts +3 -0
- package/src/resources/beta/responses/ws-base.ts +40 -6
- package/src/resources/beta/responses/ws.ts +37 -27
- package/src/resources/chat/completions/completions.ts +1 -1
- package/src/resources/evals/runs/runs.ts +1 -1
- package/src/resources/live/forks/forks.ts +6 -1
- package/src/resources/live/live.ts +6 -1
- package/src/resources/responses/ws-base.ts +40 -6
- package/src/resources/responses/ws.ts +37 -27
- package/src/tsconfig.dist-src-node.d.ts +7 -0
- package/src/tsconfig.dist-src.d.ts +3 -1
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/src/client.ts
CHANGED
|
@@ -48,7 +48,11 @@ import {
|
|
|
48
48
|
import * as Uploads from './core/uploads';
|
|
49
49
|
import * as API from './resources/index';
|
|
50
50
|
import { APIPromise } from './core/api-promise';
|
|
51
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
getDeferredRealtimeAPIKeyCache,
|
|
53
|
+
resolveRealtimeAPIKey,
|
|
54
|
+
validateCapturedAPIKey,
|
|
55
|
+
} from './internal/realtime-credentials';
|
|
52
56
|
import {
|
|
53
57
|
Batch,
|
|
54
58
|
BatchCreateParams,
|
|
@@ -258,6 +262,7 @@ import {
|
|
|
258
262
|
import { type Fetch } from './internal/builtin-types';
|
|
259
263
|
import { isRunningInBrowser } from './internal/detect-platform';
|
|
260
264
|
import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers';
|
|
265
|
+
import { mergeWebSocketAuthHeaders, webSocketHeaderRemovals } from './internal/ws';
|
|
261
266
|
import { configureProvider, type Provider, type ProviderRuntime } from './internal/provider';
|
|
262
267
|
import { FinalRequestOptions, RequestOptions } from './internal/request-options';
|
|
263
268
|
import { readEnv } from './internal/utils/env';
|
|
@@ -490,6 +495,8 @@ export class OpenAI {
|
|
|
490
495
|
protected idempotencyHeader?: string;
|
|
491
496
|
protected _options: ClientOptions;
|
|
492
497
|
private _provider: ProviderRuntime | undefined;
|
|
498
|
+
private _apiKeyInvocation = 0;
|
|
499
|
+
private _lastCachedAPIKeyInvocation = 0;
|
|
493
500
|
private _workloadIdentityAuth?: WorkloadIdentityAuth | X509WorkloadIdentityAuth;
|
|
494
501
|
|
|
495
502
|
/**
|
|
@@ -741,18 +748,33 @@ export class OpenAI {
|
|
|
741
748
|
}
|
|
742
749
|
|
|
743
750
|
/** @internal Client request headers for each new WebSocket handshake. */
|
|
744
|
-
_buildWebSocketHeaders(
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
},
|
|
751
|
+
_buildWebSocketHeaders(
|
|
752
|
+
authHeaders: Record<string, string>,
|
|
753
|
+
removedHeaders?: Set<string>,
|
|
754
|
+
): Record<string, string> {
|
|
755
|
+
const context = webSocketHeaderRemovals.get(this);
|
|
756
|
+
if (context?.baseHeaders) {
|
|
757
|
+
return mergeWebSocketAuthHeaders(
|
|
758
|
+
{ headers: context.baseHeaders },
|
|
752
759
|
authHeaders,
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
760
|
+
context.removedHeaders ?? new Set(),
|
|
761
|
+
).headers;
|
|
762
|
+
}
|
|
763
|
+
const headers = buildHeaders([
|
|
764
|
+
{
|
|
765
|
+
'User-Agent': this.getUserAgent(),
|
|
766
|
+
'OpenAI-Organization': this.organization,
|
|
767
|
+
'OpenAI-Project': this.project,
|
|
768
|
+
},
|
|
769
|
+
authHeaders,
|
|
770
|
+
this._options.defaultHeaders,
|
|
771
|
+
]);
|
|
772
|
+
headers.nulls.forEach((name) => (removedHeaders ?? context?.removedHeaders)?.add(name));
|
|
773
|
+
const result = Object.fromEntries(headers.values);
|
|
774
|
+
if (context) {
|
|
775
|
+
context.baseHeaders = { ...result };
|
|
776
|
+
}
|
|
777
|
+
return result;
|
|
756
778
|
}
|
|
757
779
|
|
|
758
780
|
protected validateHeaders(
|
|
@@ -877,11 +899,8 @@ export class OpenAI {
|
|
|
877
899
|
}
|
|
878
900
|
|
|
879
901
|
/**
|
|
880
|
-
* Resolves a
|
|
881
|
-
*
|
|
882
|
-
* invocation's key before another request updates the shared `apiKey` property.
|
|
883
|
-
* Overrides should forward `capture` or invoke it with their own resolved key
|
|
884
|
-
* to preserve invocation-local credentials in concurrent requests and Realtime factories.
|
|
902
|
+
* Resolves and retains a provider key, returning whether a provider was invoked.
|
|
903
|
+
* Overrides should forward `capture` (or call it with their resolved key) for local credentials.
|
|
885
904
|
* @internal
|
|
886
905
|
*/
|
|
887
906
|
async _callApiKey(capture?: (apiKey: string | null) => void): Promise<boolean> {
|
|
@@ -896,6 +915,8 @@ export class OpenAI {
|
|
|
896
915
|
return false;
|
|
897
916
|
}
|
|
898
917
|
|
|
918
|
+
const deferredCache = getDeferredRealtimeAPIKeyCache(this);
|
|
919
|
+
const invocation = ++this._apiKeyInvocation;
|
|
899
920
|
let token: unknown;
|
|
900
921
|
try {
|
|
901
922
|
token = await apiKey();
|
|
@@ -913,8 +934,23 @@ export class OpenAI {
|
|
|
913
934
|
`Expected 'apiKey' function argument to return a string but it returned ${token}`,
|
|
914
935
|
);
|
|
915
936
|
}
|
|
916
|
-
|
|
917
|
-
|
|
937
|
+
const resolvedToken = token;
|
|
938
|
+
const commit = () => {
|
|
939
|
+
if (capture) validateCapturedAPIKey(this, resolvedToken);
|
|
940
|
+
if (invocation < this._lastCachedAPIKeyInvocation) {
|
|
941
|
+
return resolvedToken;
|
|
942
|
+
}
|
|
943
|
+
this.apiKey = resolvedToken;
|
|
944
|
+
const cached = capture ? this.apiKey : resolvedToken;
|
|
945
|
+
this._lastCachedAPIKeyInvocation = invocation;
|
|
946
|
+
return cached;
|
|
947
|
+
};
|
|
948
|
+
if (deferredCache) {
|
|
949
|
+
deferredCache.providerKey = resolvedToken;
|
|
950
|
+
deferredCache.commit = commit;
|
|
951
|
+
}
|
|
952
|
+
const cached = deferredCache ? validateCapturedAPIKey(this, resolvedToken) : commit();
|
|
953
|
+
capture?.(cached);
|
|
918
954
|
return true;
|
|
919
955
|
}
|
|
920
956
|
|
package/src/core/streaming.ts
CHANGED
|
@@ -301,6 +301,15 @@ export class Stream<Item> implements AsyncIterable<Item> {
|
|
|
301
301
|
return this.iterator();
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
+
/** Beta-only iterator decoration for Agents helpers, preserving custom stream identity.
|
|
305
|
+
* @internal
|
|
306
|
+
*/
|
|
307
|
+
__betaTransformIterator(
|
|
308
|
+
transform: (iterator: () => AsyncIterator<Item>) => () => AsyncIterator<Item>,
|
|
309
|
+
): void {
|
|
310
|
+
this.iterator = transform(this.iterator.bind(this));
|
|
311
|
+
}
|
|
312
|
+
|
|
304
313
|
/**
|
|
305
314
|
* Splits the stream into two streams which can be
|
|
306
315
|
* independently read from at different speeds.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { standardTextFormat } from '../../standard-schema';
|
|
2
|
+
import type { JSONSchema } from '../../../lib/jsonschema';
|
|
3
|
+
import { agentOutputFormat } from '../../../lib/beta/agents/output-format';
|
|
4
|
+
|
|
5
|
+
/** Beta: bind a synchronous Standard Schema validator to Agents output. */
|
|
6
|
+
export function standardAgentTextFormat<S extends Parameters<typeof standardTextFormat>[0]>(
|
|
7
|
+
schema: S,
|
|
8
|
+
jsonSchema?: JSONSchema,
|
|
9
|
+
) {
|
|
10
|
+
const format = standardTextFormat(schema, 'agent_output', { schema: jsonSchema });
|
|
11
|
+
// SAFETY: The native Responses helper owns Standard Schema conversion and validation.
|
|
12
|
+
return agentOutputFormat(format.schema as JSONSchema, format.$parseRaw);
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { zodTextFormat } from '../../zod';
|
|
2
|
+
import type { JSONSchema } from '../../../lib/jsonschema';
|
|
3
|
+
import { agentOutputFormat } from '../../../lib/beta/agents/output-format';
|
|
4
|
+
|
|
5
|
+
/** Beta: bind a Zod v3/v4/Mini schema to an Agents text format and completed result. */
|
|
6
|
+
export function zodAgentTextFormat<S extends Parameters<typeof zodTextFormat>[0]>(schema: S) {
|
|
7
|
+
const format = zodTextFormat(schema, 'agent_output');
|
|
8
|
+
// SAFETY: The native Responses helper owns Zod schema conversion and validation.
|
|
9
|
+
return agentOutputFormat(format.schema as JSONSchema, format.$parseRaw);
|
|
10
|
+
}
|
|
@@ -1,4 +1,44 @@
|
|
|
1
1
|
import type { OpenAI } from '../client';
|
|
2
|
+
import { assertValidBedrockBearerCredential, brand_privateBedrockClient } from './bedrock';
|
|
3
|
+
|
|
4
|
+
type CaptureAPIKey = (apiKey: string | null) => void;
|
|
5
|
+
export interface DeferredAPIKeyCache {
|
|
6
|
+
client: Pick<OpenAI, 'apiKey'>;
|
|
7
|
+
providerKey?: string;
|
|
8
|
+
commit: () => string | null;
|
|
9
|
+
}
|
|
10
|
+
interface RealtimeAPIKeyCacheContext {
|
|
11
|
+
run: <T>(cache: DeferredAPIKeyCache | undefined, operation: () => T) => T;
|
|
12
|
+
getStore: () => DeferredAPIKeyCache | undefined;
|
|
13
|
+
}
|
|
14
|
+
// Only the key crosses module formats: the context belongs to each participating client.
|
|
15
|
+
const realtimeCacheContext = Symbol.for('openai.realtimeAPIKeyCacheContext');
|
|
16
|
+
type CredentialClient = Pick<OpenAI, 'apiKey'> & {
|
|
17
|
+
[realtimeCacheContext]?: RealtimeAPIKeyCacheContext;
|
|
18
|
+
};
|
|
19
|
+
let cacheContext: RealtimeAPIKeyCacheContext | undefined;
|
|
20
|
+
|
|
21
|
+
/** Installs the Node transport's invocation context without loading Node in the base client. @internal */
|
|
22
|
+
export function setRealtimeAPIKeyCacheContext(context: RealtimeAPIKeyCacheContext): void {
|
|
23
|
+
cacheContext = context;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Reserves a deferred commit when the base hook is entered for this invocation. @internal */
|
|
27
|
+
export function getDeferredRealtimeAPIKeyCache(client: CredentialClient): DeferredAPIKeyCache | undefined {
|
|
28
|
+
const deferred = client[realtimeCacheContext]?.getStore();
|
|
29
|
+
return deferred?.client === client ? deferred : undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Applies the Bedrock getter's validation when a captured credential does not enter the cache. @internal */
|
|
33
|
+
export function validateCapturedAPIKey(
|
|
34
|
+
client: Pick<OpenAI, 'apiKey'>,
|
|
35
|
+
credential: string | null,
|
|
36
|
+
): string | null {
|
|
37
|
+
if (credential !== null && brand_privateBedrockClient in client) {
|
|
38
|
+
assertValidBedrockBearerCredential(credential);
|
|
39
|
+
}
|
|
40
|
+
return credential;
|
|
41
|
+
}
|
|
2
42
|
|
|
3
43
|
/** Selects a captured credential without treating an explicit null as absent. @internal */
|
|
4
44
|
export function getRealtimeAPIKey(
|
|
@@ -15,13 +55,39 @@ export function getRealtimeAPIKey(
|
|
|
15
55
|
* synchronizing concurrent credential updates.
|
|
16
56
|
* @internal
|
|
17
57
|
*/
|
|
18
|
-
export async function resolveRealtimeAPIKey(
|
|
58
|
+
export async function resolveRealtimeAPIKey(
|
|
59
|
+
client: CredentialClient & Pick<OpenAI, '_callApiKey'>,
|
|
60
|
+
deferCache = false,
|
|
61
|
+
): Promise<{
|
|
19
62
|
apiKey: string | null;
|
|
20
63
|
isProvider: boolean;
|
|
64
|
+
commit: () => string | null;
|
|
21
65
|
}> {
|
|
22
66
|
let apiKey: string | null | undefined;
|
|
23
|
-
const
|
|
67
|
+
const current: DeferredAPIKeyCache = {
|
|
68
|
+
client,
|
|
69
|
+
commit: () => (apiKey === undefined ? client.apiKey : apiKey),
|
|
70
|
+
};
|
|
71
|
+
const capture: CaptureAPIKey = (resolved) => {
|
|
24
72
|
apiKey = resolved;
|
|
25
|
-
}
|
|
26
|
-
|
|
73
|
+
};
|
|
74
|
+
const invoke = () => client._callApiKey(capture);
|
|
75
|
+
const context = client[realtimeCacheContext] ?? cacheContext;
|
|
76
|
+
if (deferCache && context && !client[realtimeCacheContext]) {
|
|
77
|
+
Object.defineProperty(client, realtimeCacheContext, { value: context });
|
|
78
|
+
}
|
|
79
|
+
// An HTTP or ordinary Realtime request nested inside a WebSocket hook owns its own cache writes.
|
|
80
|
+
const isProvider = await (context ? context.run(deferCache ? current : undefined, invoke) : invoke());
|
|
81
|
+
return {
|
|
82
|
+
apiKey: apiKey === undefined ? client.apiKey : apiKey,
|
|
83
|
+
isProvider,
|
|
84
|
+
commit: () => {
|
|
85
|
+
const hookKey =
|
|
86
|
+
current.providerKey !== undefined && apiKey !== undefined && apiKey !== current.providerKey
|
|
87
|
+
? validateCapturedAPIKey(client, apiKey)
|
|
88
|
+
: undefined;
|
|
89
|
+
const cached = current.commit();
|
|
90
|
+
return hookKey === undefined ? validateCapturedAPIKey(client, cached) : hookKey;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
27
93
|
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { OpenAIError } from '../core/error';
|
|
2
|
+
import type { OpenAI } from '../client';
|
|
3
|
+
import { resolveRealtimeAPIKey } from './realtime-credentials';
|
|
4
|
+
import {
|
|
5
|
+
buildWebSocketOptions,
|
|
6
|
+
mergeWebSocketAuthHeaders,
|
|
7
|
+
snapshotWebSocketCredentials,
|
|
8
|
+
WEBSOCKET_METADATA_HEADERS,
|
|
9
|
+
} from './ws';
|
|
10
|
+
import type { CredentialedWebSocketOptions } from './ws';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Keep caller credential admission distinct from the hook's final provider-key transform.
|
|
14
|
+
* The latter reuses captured base headers so no caller getters are reread after the refresh.
|
|
15
|
+
*/
|
|
16
|
+
function buildResponsesWebSocketOptions<Options extends CredentialedWebSocketOptions>(
|
|
17
|
+
client: OpenAI,
|
|
18
|
+
authHeaders: Record<string, string>,
|
|
19
|
+
options: Options | null | undefined,
|
|
20
|
+
prepared?: {
|
|
21
|
+
options: ReturnType<typeof buildWebSocketOptions>;
|
|
22
|
+
removedHeaders: Set<string>;
|
|
23
|
+
apiKey?: string | null | undefined;
|
|
24
|
+
},
|
|
25
|
+
) {
|
|
26
|
+
if (prepared) {
|
|
27
|
+
return prepared.apiKey === undefined
|
|
28
|
+
? mergeWebSocketAuthHeaders(prepared.options, authHeaders, prepared.removedHeaders)
|
|
29
|
+
: buildWebSocketOptions(client, authHeaders, prepared.options, prepared.removedHeaders, true);
|
|
30
|
+
}
|
|
31
|
+
if (!client._hasApiKeyProvider()) {
|
|
32
|
+
return buildWebSocketOptions(client, authHeaders, options);
|
|
33
|
+
}
|
|
34
|
+
const removedHeaders = new Set<string>();
|
|
35
|
+
const provided = buildWebSocketOptions(client, {}, options, removedHeaders);
|
|
36
|
+
return snapshotWebSocketCredentials(provided, WEBSOCKET_METADATA_HEADERS)
|
|
37
|
+
? provided
|
|
38
|
+
: buildWebSocketOptions(client, authHeaders, provided, removedHeaders, true);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Classify reconnect credentials without treating the previous SDK key as a caller override.
|
|
43
|
+
* Capture routing and caller options before any asynchronous key refresh.
|
|
44
|
+
*/
|
|
45
|
+
function prepareResponsesWebSocketReconnect<Options extends CredentialedWebSocketOptions>(
|
|
46
|
+
client: OpenAI,
|
|
47
|
+
socketOptions: Options | null | undefined,
|
|
48
|
+
initialAuthUsedAPIKey: boolean,
|
|
49
|
+
) {
|
|
50
|
+
const removedHeaders = new Set<string>();
|
|
51
|
+
const options = buildWebSocketOptions(client, {}, socketOptions, removedHeaders);
|
|
52
|
+
let authentication: 'provided' | 'cached' | 'refresh';
|
|
53
|
+
if (snapshotWebSocketCredentials(options, WEBSOCKET_METADATA_HEADERS)) {
|
|
54
|
+
authentication = 'provided';
|
|
55
|
+
} else if (
|
|
56
|
+
removedHeaders.has('authorization') ||
|
|
57
|
+
options.headers?.['authorization'] !== undefined ||
|
|
58
|
+
!initialAuthUsedAPIKey
|
|
59
|
+
) {
|
|
60
|
+
authentication = 'cached';
|
|
61
|
+
} else {
|
|
62
|
+
authentication = 'refresh';
|
|
63
|
+
}
|
|
64
|
+
return { options, removedHeaders, authentication };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Per-connection Responses credential state shared by the stable and beta Node transports. */
|
|
68
|
+
export class ResponsesWebSocketCredentials {
|
|
69
|
+
private initialAuthUsedAPIKey = false;
|
|
70
|
+
private initialAPIKey: string | undefined;
|
|
71
|
+
private prepared:
|
|
72
|
+
| {
|
|
73
|
+
options: ReturnType<typeof buildWebSocketOptions>;
|
|
74
|
+
removedHeaders: Set<string>;
|
|
75
|
+
apiKey?: string | null | undefined;
|
|
76
|
+
}
|
|
77
|
+
| undefined;
|
|
78
|
+
|
|
79
|
+
get preparedAPIKey(): string | null | undefined {
|
|
80
|
+
return this.prepared?.apiKey;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
trackInitialHeaders(client: OpenAI, headers: Record<string, string>, hasSocket: () => boolean): void {
|
|
84
|
+
if (!hasSocket() && client._hasApiKeyProvider()) {
|
|
85
|
+
this.initialAPIKey = headers['Authorization']?.slice('Bearer '.length);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
usesAPIKey(client: OpenAI, headers: Record<string, string>): boolean {
|
|
90
|
+
// Hooks can read the SDK key without delegating. Only use an ordinary cached property here:
|
|
91
|
+
// invoking custom/Bedrock getters could validate credentials a caller-owned socket never sends.
|
|
92
|
+
const apiKey =
|
|
93
|
+
this.initialAPIKey ??
|
|
94
|
+
(client._hasApiKeyProvider() ? Object.getOwnPropertyDescriptor(client, 'apiKey')?.value : undefined);
|
|
95
|
+
if (!apiKey) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
// Metadata containing a copy of a credential does not make it the socket's authentication.
|
|
99
|
+
return snapshotWebSocketCredentials(
|
|
100
|
+
{
|
|
101
|
+
headers: Object.fromEntries(
|
|
102
|
+
Object.entries(headers).map(([name, value]) => [name, value?.includes(apiKey) ? value : '']),
|
|
103
|
+
),
|
|
104
|
+
},
|
|
105
|
+
WEBSOCKET_METADATA_HEADERS,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
build<Options extends CredentialedWebSocketOptions>(
|
|
110
|
+
client: OpenAI,
|
|
111
|
+
authHeaders: Record<string, string>,
|
|
112
|
+
options: Options | null | undefined,
|
|
113
|
+
initialAuthUsedAPIKey?: boolean,
|
|
114
|
+
) {
|
|
115
|
+
if (initialAuthUsedAPIKey !== undefined) {
|
|
116
|
+
this.initialAuthUsedAPIKey = initialAuthUsedAPIKey;
|
|
117
|
+
this.initialAPIKey = undefined;
|
|
118
|
+
}
|
|
119
|
+
const socketOptions = buildResponsesWebSocketOptions(client, authHeaders, options, this.prepared);
|
|
120
|
+
if (
|
|
121
|
+
client._hasApiKeyProvider() &&
|
|
122
|
+
!authHeaders['Authorization'] &&
|
|
123
|
+
!snapshotWebSocketCredentials(socketOptions, WEBSOCKET_METADATA_HEADERS)
|
|
124
|
+
) {
|
|
125
|
+
throw new OpenAIError(
|
|
126
|
+
'Cannot open a Responses WebSocket with an unresolved function-based apiKey. Resolve it before constructing the WebSocket or provide explicit WebSocket credentials.',
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return socketOptions;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async prepare<Socket, Options extends CredentialedWebSocketOptions>(
|
|
133
|
+
client: OpenAI,
|
|
134
|
+
socketOptions: Options | null | undefined,
|
|
135
|
+
authHeaders: (apiKey?: string | null) => Record<string, string>,
|
|
136
|
+
createSocket: (url: URL, headers: Record<string, string>) => Socket,
|
|
137
|
+
): Promise<(url: URL) => Socket> {
|
|
138
|
+
const { options, removedHeaders, authentication } = prepareResponsesWebSocketReconnect(
|
|
139
|
+
client,
|
|
140
|
+
socketOptions,
|
|
141
|
+
this.initialAuthUsedAPIKey,
|
|
142
|
+
);
|
|
143
|
+
const createPreparedSocket = (url: URL, headers: Record<string, string>, apiKey?: string | null) => {
|
|
144
|
+
const previous = this.prepared;
|
|
145
|
+
this.prepared = { options, removedHeaders, apiKey };
|
|
146
|
+
try {
|
|
147
|
+
// Resolve headers inside the scope so legacy zero-argument overrides see this attempt's key.
|
|
148
|
+
return createSocket(url, apiKey === undefined ? headers : authHeaders(apiKey));
|
|
149
|
+
} finally {
|
|
150
|
+
this.prepared = previous;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
if (authentication !== 'refresh') {
|
|
154
|
+
const headers = authentication === 'provided' ? {} : authHeaders();
|
|
155
|
+
return (url) => createPreparedSocket(url, headers);
|
|
156
|
+
}
|
|
157
|
+
const { commit } = await resolveRealtimeAPIKey(client, true);
|
|
158
|
+
return (url) => createPreparedSocket(url, {}, commit());
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -1,10 +1,76 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
1
2
|
import type * as WS from 'ws';
|
|
2
3
|
import type { WebSocketLike } from './ws-adapter';
|
|
3
4
|
import { protectWebSocketOptionsFromCredentialRedirects } from './ws';
|
|
5
|
+
import { setRealtimeAPIKeyCacheContext } from './realtime-credentials';
|
|
6
|
+
import type { DeferredAPIKeyCache } from './realtime-credentials';
|
|
7
|
+
|
|
8
|
+
setRealtimeAPIKeyCacheContext(new AsyncLocalStorage<DeferredAPIKeyCache | undefined>());
|
|
4
9
|
|
|
5
10
|
/** A generic event listener callback. */
|
|
6
11
|
type Listener = (...args: any[]) => void;
|
|
7
12
|
|
|
13
|
+
function copyTLSMaterial(value: string | Buffer): string | Buffer {
|
|
14
|
+
return ArrayBuffer.isView(value)
|
|
15
|
+
? Buffer.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength))
|
|
16
|
+
: value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Snapshot caller headers and TLS identity before an async reconnect credential refresh. */
|
|
20
|
+
export function snapshotNodeWebSocketOptions(
|
|
21
|
+
options: WS.ClientOptions | null | undefined,
|
|
22
|
+
): Omit<WS.ClientOptions, 'headers'> & { headers: Record<string, string | string[]> } {
|
|
23
|
+
const captured = {
|
|
24
|
+
...options,
|
|
25
|
+
headers: Object.fromEntries(
|
|
26
|
+
Object.entries(options?.headers ?? {}).map(([name, value]) => [
|
|
27
|
+
name,
|
|
28
|
+
Array.isArray(value) ? [...value] : value,
|
|
29
|
+
]),
|
|
30
|
+
),
|
|
31
|
+
};
|
|
32
|
+
const { ca, cert, crl, key, pfx } = captured;
|
|
33
|
+
if (ca !== undefined) {
|
|
34
|
+
captured.ca = Array.isArray(ca) ? ca.map(copyTLSMaterial) : copyTLSMaterial(ca);
|
|
35
|
+
}
|
|
36
|
+
if (cert !== undefined) {
|
|
37
|
+
captured.cert = Array.isArray(cert) ? cert.map(copyTLSMaterial) : copyTLSMaterial(cert);
|
|
38
|
+
}
|
|
39
|
+
if (crl !== undefined) {
|
|
40
|
+
captured.crl = Array.isArray(crl) ? crl.map(copyTLSMaterial) : copyTLSMaterial(crl);
|
|
41
|
+
}
|
|
42
|
+
if (key !== undefined) {
|
|
43
|
+
captured.key = Array.isArray(key)
|
|
44
|
+
? key.map((value) =>
|
|
45
|
+
typeof value === 'string' || ArrayBuffer.isView(value)
|
|
46
|
+
? copyTLSMaterial(value)
|
|
47
|
+
: { ...value, pem: copyTLSMaterial(value.pem) },
|
|
48
|
+
)
|
|
49
|
+
: copyTLSMaterial(key);
|
|
50
|
+
}
|
|
51
|
+
if (pfx !== undefined) {
|
|
52
|
+
captured.pfx = Array.isArray(pfx)
|
|
53
|
+
? pfx.map((value) =>
|
|
54
|
+
typeof value === 'string' || ArrayBuffer.isView(value)
|
|
55
|
+
? copyTLSMaterial(value)
|
|
56
|
+
: { ...value, buf: copyTLSMaterial(value.buf) },
|
|
57
|
+
)
|
|
58
|
+
: copyTLSMaterial(pfx);
|
|
59
|
+
}
|
|
60
|
+
if ('session' in captured && Buffer.isBuffer(captured.session)) {
|
|
61
|
+
captured.session = Buffer.from(captured.session);
|
|
62
|
+
}
|
|
63
|
+
if ('ALPNProtocols' in captured) {
|
|
64
|
+
if (Array.isArray(captured.ALPNProtocols)) {
|
|
65
|
+
captured.ALPNProtocols = [...captured.ALPNProtocols];
|
|
66
|
+
} else if (ArrayBuffer.isView(captured.ALPNProtocols)) {
|
|
67
|
+
const { buffer, byteOffset, byteLength } = captured.ALPNProtocols;
|
|
68
|
+
captured.ALPNProtocols = Buffer.from(new Uint8Array(buffer, byteOffset, byteLength));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return captured;
|
|
72
|
+
}
|
|
73
|
+
|
|
8
74
|
/**
|
|
9
75
|
* Adapts a Node.js `ws` socket to the SDK's platform-neutral WebSocket API.
|
|
10
76
|
*
|
package/src/internal/ws.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { concatBytes, encodeUTF8 } from './utils/bytes';
|
|
2
2
|
import { OpenAIError } from '../core/error';
|
|
3
3
|
import type { WebSocketLike } from './ws-adapter';
|
|
4
|
+
import type { OpenAI } from '../client';
|
|
4
5
|
|
|
5
6
|
const webSocketErrors = new WeakMap<WebSocketLike, Error>();
|
|
6
7
|
|
|
@@ -73,7 +74,7 @@ export type ReconnectingOverrides<Parameters = Record<string, unknown>> =
|
|
|
73
74
|
*/
|
|
74
75
|
export type RawWebSocketData = string | ArrayBufferLike | ArrayBufferView | ArrayBufferView[];
|
|
75
76
|
|
|
76
|
-
interface CredentialedWebSocketOptions {
|
|
77
|
+
export interface CredentialedWebSocketOptions {
|
|
77
78
|
auth?: string | null | undefined;
|
|
78
79
|
followRedirects?: boolean | undefined;
|
|
79
80
|
headers?: object | null | undefined;
|
|
@@ -99,19 +100,57 @@ const REDIRECT_SAFE_WEBSOCKET_HEADERS = new Set([
|
|
|
99
100
|
'x-trace-id',
|
|
100
101
|
]);
|
|
101
102
|
|
|
103
|
+
const WEBSOCKET_METADATA_HEADER_NAMES = new Set([
|
|
104
|
+
'accept',
|
|
105
|
+
'accept-encoding',
|
|
106
|
+
'accept-language',
|
|
107
|
+
'content-type',
|
|
108
|
+
'x-request-id',
|
|
109
|
+
'x-client-request-id',
|
|
110
|
+
'x-correlation-id',
|
|
111
|
+
'traceparent',
|
|
112
|
+
'tracestate',
|
|
113
|
+
'sentry-trace',
|
|
114
|
+
'x-amzn-trace-id',
|
|
115
|
+
'x-cloud-trace-context',
|
|
116
|
+
'x-datadog-trace-id',
|
|
117
|
+
'x-datadog-parent-id',
|
|
118
|
+
'x-datadog-sampling-priority',
|
|
119
|
+
'x-datadog-origin',
|
|
120
|
+
'x-datadog-tags',
|
|
121
|
+
'baggage',
|
|
122
|
+
'b3',
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
// Request and tracing metadata do not authenticate a Responses socket, but remain protected on redirects.
|
|
126
|
+
export const WEBSOCKET_METADATA_HEADERS = {
|
|
127
|
+
has: (name: string, values: readonly unknown[]): boolean =>
|
|
128
|
+
WEBSOCKET_METADATA_HEADER_NAMES.has(name) ||
|
|
129
|
+
name.startsWith('x-b3-') ||
|
|
130
|
+
(name === 'sec-websocket-protocol' &&
|
|
131
|
+
!values.some(
|
|
132
|
+
(value) =>
|
|
133
|
+
typeof value === 'string' &&
|
|
134
|
+
value.split(',').some((protocol) => protocol.trim().startsWith('openai-insecure-api-key.')),
|
|
135
|
+
)),
|
|
136
|
+
};
|
|
137
|
+
|
|
102
138
|
function isWebSocketCredentialHeader(name: string): boolean {
|
|
103
139
|
return !REDIRECT_SAFE_WEBSOCKET_HEADERS.has(name.toLowerCase().split('_').join('-'));
|
|
104
140
|
}
|
|
105
141
|
|
|
106
142
|
/**
|
|
107
143
|
* Snapshots credential values in final socket options before validation and dispatch.
|
|
108
|
-
* Reports potential caller authentication,
|
|
109
|
-
* remains responsible for validating credentials. Noncredential headers are left intact.
|
|
144
|
+
* Reports potential caller authentication, excluding additional metadata when requested.
|
|
145
|
+
* The server remains responsible for validating credentials. Noncredential headers are left intact.
|
|
110
146
|
*/
|
|
111
|
-
export function snapshotWebSocketCredentials(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
147
|
+
export function snapshotWebSocketCredentials(
|
|
148
|
+
options: {
|
|
149
|
+
auth?: unknown;
|
|
150
|
+
headers?: Record<string, unknown> | undefined;
|
|
151
|
+
},
|
|
152
|
+
metadataHeaders?: { has: (name: string, values: readonly unknown[]) => boolean },
|
|
153
|
+
): boolean {
|
|
115
154
|
if (options.auth !== null && options.auth !== undefined) {
|
|
116
155
|
options.auth = String(options.auth);
|
|
117
156
|
}
|
|
@@ -135,10 +174,12 @@ export function snapshotWebSocketCredentials(options: {
|
|
|
135
174
|
}
|
|
136
175
|
headers[name] = snapshot;
|
|
137
176
|
const values = Array.isArray(snapshot) ? snapshot : [snapshot];
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
177
|
+
if (!metadataHeaders?.has(normalizedName, values)) {
|
|
178
|
+
credentials.set(
|
|
179
|
+
name.toLowerCase(),
|
|
180
|
+
values.some((item) => typeof item === 'string' && item.trim().length > 0),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
142
183
|
}
|
|
143
184
|
// Node applies header names case-insensitively, and Authorization overrides Basic auth.
|
|
144
185
|
return (
|
|
@@ -147,6 +188,93 @@ export function snapshotWebSocketCredentials(options: {
|
|
|
147
188
|
);
|
|
148
189
|
}
|
|
149
190
|
|
|
191
|
+
/** Merge transport authentication before explicit caller overrides and header removals. */
|
|
192
|
+
export function mergeWebSocketAuthHeaders<Options extends CredentialedWebSocketOptions>(
|
|
193
|
+
options: Options,
|
|
194
|
+
authHeaders: Record<string, string>,
|
|
195
|
+
removedHeaders: ReadonlySet<string>,
|
|
196
|
+
) {
|
|
197
|
+
const headers = new Map(Object.entries(authHeaders).map(([name, value]) => [name.toLowerCase(), value]));
|
|
198
|
+
for (const name of removedHeaders) {
|
|
199
|
+
headers.delete(name);
|
|
200
|
+
}
|
|
201
|
+
for (const [name, value] of Object.entries(options.headers ?? {})) {
|
|
202
|
+
headers.set(name, value);
|
|
203
|
+
}
|
|
204
|
+
return { ...options, headers: Object.fromEntries(headers) };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Removal sink for one synchronous header-hook call, including legacy one-argument overrides. */
|
|
208
|
+
export const webSocketHeaderRemovals = new WeakMap<
|
|
209
|
+
OpenAI,
|
|
210
|
+
{ removedHeaders: Set<string> | undefined; baseHeaders: Record<string, string> | undefined }
|
|
211
|
+
>();
|
|
212
|
+
const preparedWebSocketHeaders = new WeakMap<
|
|
213
|
+
object,
|
|
214
|
+
{ baseHeaders: Record<string, string> | undefined; transportHeaders: Map<string, boolean> }
|
|
215
|
+
>();
|
|
216
|
+
|
|
217
|
+
/** Build a WebSocket handshake with explicit caller headers applied after the client defaults. */
|
|
218
|
+
export function buildWebSocketOptions<Options extends Pick<CredentialedWebSocketOptions, 'headers'>>(
|
|
219
|
+
client: OpenAI,
|
|
220
|
+
authHeaders: Record<string, string>,
|
|
221
|
+
options: Options | null | undefined,
|
|
222
|
+
removedHeaders?: Set<string>,
|
|
223
|
+
usePreparedHeaders = false,
|
|
224
|
+
) {
|
|
225
|
+
// Capture the base result even through older overrides that forward only one argument.
|
|
226
|
+
const previous = webSocketHeaderRemovals.get(client);
|
|
227
|
+
const prepared = usePreparedHeaders && options ? preparedWebSocketHeaders.get(options) : undefined;
|
|
228
|
+
const context: NonNullable<ReturnType<typeof webSocketHeaderRemovals.get>> = {
|
|
229
|
+
removedHeaders,
|
|
230
|
+
baseHeaders: prepared?.baseHeaders,
|
|
231
|
+
};
|
|
232
|
+
webSocketHeaderRemovals.set(client, context);
|
|
233
|
+
let headers: Map<string, string>;
|
|
234
|
+
try {
|
|
235
|
+
headers = new Map(
|
|
236
|
+
Object.entries(client._buildWebSocketHeaders(authHeaders, removedHeaders)).map(([name, value]) => [
|
|
237
|
+
name.toLowerCase(),
|
|
238
|
+
value,
|
|
239
|
+
]),
|
|
240
|
+
);
|
|
241
|
+
} finally {
|
|
242
|
+
if (previous) {
|
|
243
|
+
webSocketHeaderRemovals.set(client, previous);
|
|
244
|
+
} else {
|
|
245
|
+
webSocketHeaderRemovals.delete(client);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// The first hook output is not a caller override of the final, credentialed hook.
|
|
249
|
+
// Save transport names and explicit nulls separately, then reuse their validated values.
|
|
250
|
+
const transportHeaders = new Map<string, boolean>();
|
|
251
|
+
const currentHeaders = new Map(Object.entries(options?.headers ?? {}));
|
|
252
|
+
const overrides = prepared
|
|
253
|
+
? [...prepared.transportHeaders].map(
|
|
254
|
+
([name, removed]) => [name, removed ? null : currentHeaders.get(name)] as const,
|
|
255
|
+
)
|
|
256
|
+
: currentHeaders;
|
|
257
|
+
for (const [name, value] of overrides) {
|
|
258
|
+
const normalizedName = name.toLowerCase();
|
|
259
|
+
if (value === null) {
|
|
260
|
+
headers.delete(normalizedName);
|
|
261
|
+
removedHeaders?.add(normalizedName);
|
|
262
|
+
transportHeaders.set(normalizedName, true);
|
|
263
|
+
} else if (value !== undefined) {
|
|
264
|
+
headers.set(normalizedName, value);
|
|
265
|
+
removedHeaders?.delete(normalizedName);
|
|
266
|
+
transportHeaders.set(normalizedName, false);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const result = {
|
|
270
|
+
...options,
|
|
271
|
+
headers: Object.fromEntries(headers),
|
|
272
|
+
followRedirects: false,
|
|
273
|
+
};
|
|
274
|
+
preparedWebSocketHeaders.set(result, { baseHeaders: context.baseHeaders, transportHeaders });
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
|
|
150
278
|
/** Prevents WebSocket redirects from forwarding caller or SDK credentials to another origin. */
|
|
151
279
|
export function protectWebSocketOptionsFromCredentialRedirects<Options extends CredentialedWebSocketOptions>(
|
|
152
280
|
options: Options,
|