openai 6.32.0 → 6.34.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 +57 -0
- package/README.md +96 -0
- package/auth/index.d.mts +4 -0
- package/auth/index.d.mts.map +1 -0
- package/auth/index.d.ts +4 -0
- package/auth/index.d.ts.map +1 -0
- package/auth/index.js +11 -0
- package/auth/index.js.map +1 -0
- package/auth/index.mjs +3 -0
- package/auth/index.mjs.map +1 -0
- package/auth/subject-token-providers.d.mts +20 -0
- package/auth/subject-token-providers.d.mts.map +1 -0
- package/auth/subject-token-providers.d.ts +20 -0
- package/auth/subject-token-providers.d.ts.map +1 -0
- package/auth/subject-token-providers.js +127 -0
- package/auth/subject-token-providers.js.map +1 -0
- package/auth/subject-token-providers.mjs +121 -0
- package/auth/subject-token-providers.mjs.map +1 -0
- package/auth/types.d.mts +23 -0
- package/auth/types.d.mts.map +1 -0
- package/auth/types.d.ts +23 -0
- package/auth/types.d.ts.map +1 -0
- package/auth/types.js +3 -0
- package/auth/types.js.map +1 -0
- package/auth/types.mjs +2 -0
- package/auth/types.mjs.map +1 -0
- package/auth/workload-identity-auth.d.mts +16 -0
- package/auth/workload-identity-auth.d.mts.map +1 -0
- package/auth/workload-identity-auth.d.ts +16 -0
- package/auth/workload-identity-auth.d.ts.map +1 -0
- package/auth/workload-identity-auth.js +92 -0
- package/auth/workload-identity-auth.js.map +1 -0
- package/auth/workload-identity-auth.mjs +87 -0
- package/auth/workload-identity-auth.mjs.map +1 -0
- package/azure.d.mts +2 -1
- package/azure.d.mts.map +1 -1
- package/azure.d.ts +2 -1
- package/azure.d.ts.map +1 -1
- package/azure.js.map +1 -1
- package/azure.mjs.map +1 -1
- package/client.d.mts +11 -1
- package/client.d.mts.map +1 -1
- package/client.d.ts +11 -1
- package/client.d.ts.map +1 -1
- package/client.js +73 -10
- package/client.js.map +1 -1
- package/client.mjs +73 -10
- package/client.mjs.map +1 -1
- package/core/error.d.mts +15 -0
- package/core/error.d.mts.map +1 -1
- package/core/error.d.ts +15 -0
- package/core/error.d.ts.map +1 -1
- package/core/error.js +34 -1
- package/core/error.js.map +1 -1
- package/core/error.mjs +31 -0
- package/core/error.mjs.map +1 -1
- package/index.d.mts +1 -1
- package/index.d.mts.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +3 -1
- package/index.js.map +1 -1
- package/index.mjs +1 -1
- package/index.mjs.map +1 -1
- package/package.json +11 -1
- package/resources/conversations/conversations.d.mts +7 -0
- package/resources/conversations/conversations.d.mts.map +1 -1
- package/resources/conversations/conversations.d.ts +7 -0
- package/resources/conversations/conversations.d.ts.map +1 -1
- package/resources/conversations/conversations.js.map +1 -1
- package/resources/conversations/conversations.mjs.map +1 -1
- package/resources/conversations/items.d.mts +1 -1
- package/resources/conversations/items.d.mts.map +1 -1
- package/resources/conversations/items.d.ts +1 -1
- package/resources/conversations/items.d.ts.map +1 -1
- package/resources/realtime/calls.d.mts +3 -2
- package/resources/realtime/calls.d.mts.map +1 -1
- package/resources/realtime/calls.d.ts +3 -2
- package/resources/realtime/calls.d.ts.map +1 -1
- package/resources/realtime/client-secrets.d.mts +3 -2
- package/resources/realtime/client-secrets.d.mts.map +1 -1
- package/resources/realtime/client-secrets.d.ts +3 -2
- package/resources/realtime/client-secrets.d.ts.map +1 -1
- package/resources/realtime/realtime.d.mts +6 -4
- package/resources/realtime/realtime.d.mts.map +1 -1
- package/resources/realtime/realtime.d.ts +6 -4
- package/resources/realtime/realtime.d.ts.map +1 -1
- package/resources/realtime/realtime.js.map +1 -1
- package/resources/realtime/realtime.mjs.map +1 -1
- package/resources/responses/internal-base.d.mts +9 -0
- package/resources/responses/internal-base.d.mts.map +1 -1
- package/resources/responses/internal-base.d.ts +9 -0
- package/resources/responses/internal-base.d.ts.map +1 -1
- package/resources/responses/internal-base.js.map +1 -1
- package/resources/responses/internal-base.mjs.map +1 -1
- package/resources/responses/responses.d.mts +153 -14
- package/resources/responses/responses.d.mts.map +1 -1
- package/resources/responses/responses.d.ts +153 -14
- package/resources/responses/responses.d.ts.map +1 -1
- package/resources/responses/responses.js.map +1 -1
- package/resources/responses/responses.mjs.map +1 -1
- package/resources/responses/ws.d.mts +26 -1
- package/resources/responses/ws.d.mts.map +1 -1
- package/resources/responses/ws.d.ts +26 -1
- package/resources/responses/ws.d.ts.map +1 -1
- package/resources/responses/ws.js +118 -0
- package/resources/responses/ws.js.map +1 -1
- package/resources/responses/ws.mjs +118 -0
- package/resources/responses/ws.mjs.map +1 -1
- package/resources/shared.d.mts +1 -0
- package/resources/shared.d.mts.map +1 -1
- package/resources/shared.d.ts +1 -0
- package/resources/shared.d.ts.map +1 -1
- package/resources/vector-stores/file-batches.d.mts +9 -5
- package/resources/vector-stores/file-batches.d.mts.map +1 -1
- package/resources/vector-stores/file-batches.d.ts +9 -5
- package/resources/vector-stores/file-batches.d.ts.map +1 -1
- package/resources/vector-stores/files.d.mts +3 -1
- package/resources/vector-stores/files.d.mts.map +1 -1
- package/resources/vector-stores/files.d.ts +3 -1
- package/resources/vector-stores/files.d.ts.map +1 -1
- package/src/auth/index.ts +9 -0
- package/src/auth/subject-token-providers.ts +185 -0
- package/src/auth/types.ts +28 -0
- package/src/auth/workload-identity-auth.ts +118 -0
- package/src/azure.ts +2 -1
- package/src/client.ts +112 -9
- package/src/core/error.ts +40 -0
- package/src/index.ts +2 -0
- package/src/resources/conversations/conversations.ts +8 -0
- package/src/resources/conversations/items.ts +1 -0
- package/src/resources/realtime/calls.ts +3 -2
- package/src/resources/realtime/client-secrets.ts +3 -2
- package/src/resources/realtime/realtime.ts +6 -4
- package/src/resources/responses/api.md +2 -0
- package/src/resources/responses/internal-base.ts +5 -1
- package/src/resources/responses/responses.ts +198 -14
- package/src/resources/responses/ws.ts +130 -1
- package/src/resources/shared.ts +2 -0
- package/src/resources/vector-stores/file-batches.ts +9 -5
- package/src/resources/vector-stores/files.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
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { WorkloadIdentity, TokenExchangeResponse } from './types';
|
|
2
|
+
import type { Fetch } from '../internal/builtin-types';
|
|
3
|
+
import * as Shims from '../internal/shims';
|
|
4
|
+
import { APIError, OAuthError } from '../core/error';
|
|
5
|
+
|
|
6
|
+
interface CachedToken {
|
|
7
|
+
token: string;
|
|
8
|
+
expiresAt: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const SUBJECT_TOKEN_TYPES: Record<string, string> = {
|
|
12
|
+
jwt: 'urn:ietf:params:oauth:token-type:jwt',
|
|
13
|
+
id: 'urn:ietf:params:oauth:token-type:id_token',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const TOKEN_EXCHANGE_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:token-exchange';
|
|
17
|
+
|
|
18
|
+
export class WorkloadIdentityAuth {
|
|
19
|
+
private cachedToken: CachedToken | null = null;
|
|
20
|
+
private refreshPromise: Promise<string> | null = null;
|
|
21
|
+
private readonly config: WorkloadIdentity;
|
|
22
|
+
private readonly tokenExchangeUrl: string = 'https://auth.openai.com/oauth/token';
|
|
23
|
+
private readonly fetch: Fetch;
|
|
24
|
+
|
|
25
|
+
constructor(config: WorkloadIdentity, fetch?: Fetch) {
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.fetch = fetch ?? Shims.getDefaultFetch();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async getToken(): Promise<string> {
|
|
31
|
+
if (!this.cachedToken || this.isTokenExpired(this.cachedToken)) {
|
|
32
|
+
if (this.refreshPromise) {
|
|
33
|
+
return await this.refreshPromise;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.refreshPromise = this.refreshToken();
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const token = await this.refreshPromise;
|
|
40
|
+
return token;
|
|
41
|
+
} finally {
|
|
42
|
+
this.refreshPromise = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (this.needsRefresh(this.cachedToken) && !this.refreshPromise) {
|
|
47
|
+
this.refreshPromise = this.refreshToken().finally(() => {
|
|
48
|
+
this.refreshPromise = null;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return this.cachedToken.token;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private async refreshToken(): Promise<string> {
|
|
56
|
+
const subjectToken = await this.config.provider.getToken();
|
|
57
|
+
|
|
58
|
+
const response = await this.fetch(this.tokenExchangeUrl, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
'Content-Type': 'application/json',
|
|
62
|
+
},
|
|
63
|
+
body: JSON.stringify({
|
|
64
|
+
grant_type: TOKEN_EXCHANGE_GRANT_TYPE,
|
|
65
|
+
client_id: this.config.clientId,
|
|
66
|
+
subject_token: subjectToken,
|
|
67
|
+
subject_token_type: SUBJECT_TOKEN_TYPES[this.config.provider.tokenType],
|
|
68
|
+
identity_provider_id: this.config.identityProviderId,
|
|
69
|
+
service_account_id: this.config.serviceAccountId,
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
const errorText = await response.text();
|
|
75
|
+
let body: any = undefined;
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
body = JSON.parse(errorText);
|
|
79
|
+
} catch {}
|
|
80
|
+
|
|
81
|
+
if (response.status === 400 || response.status === 401 || response.status === 403) {
|
|
82
|
+
throw new OAuthError(response.status as 400 | 401 | 403, body, response.headers);
|
|
83
|
+
}
|
|
84
|
+
throw APIError.generate(
|
|
85
|
+
response.status,
|
|
86
|
+
body,
|
|
87
|
+
`Token exchange failed with status ${response.status}`,
|
|
88
|
+
response.headers,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const tokenResponse = (await response.json()) as TokenExchangeResponse;
|
|
93
|
+
const expiresIn = tokenResponse.expires_in || 3600;
|
|
94
|
+
const expiresAt = Date.now() + expiresIn * 1000;
|
|
95
|
+
|
|
96
|
+
this.cachedToken = {
|
|
97
|
+
token: tokenResponse.access_token,
|
|
98
|
+
expiresAt,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return tokenResponse.access_token;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private isTokenExpired(cachedToken: CachedToken): boolean {
|
|
105
|
+
return Date.now() >= cachedToken.expiresAt;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private needsRefresh(cachedToken: CachedToken): boolean {
|
|
109
|
+
const bufferSeconds = this.config.refreshBufferSeconds ?? 1200;
|
|
110
|
+
const bufferMs = bufferSeconds * 1000;
|
|
111
|
+
return Date.now() >= cachedToken.expiresAt - bufferMs;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
invalidateToken(): void {
|
|
115
|
+
this.cachedToken = null;
|
|
116
|
+
this.refreshPromise = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
package/src/azure.ts
CHANGED
|
@@ -4,7 +4,8 @@ import { buildHeaders } from './internal/headers';
|
|
|
4
4
|
import * as Errors from './error';
|
|
5
5
|
import { FinalRequestOptions } from './internal/request-options';
|
|
6
6
|
import { isObj, readEnv } from './internal/utils';
|
|
7
|
-
import {
|
|
7
|
+
import { OpenAI } from './client';
|
|
8
|
+
import type { ClientOptions } from './client';
|
|
8
9
|
|
|
9
10
|
/** API Client for interfacing with the Azure OpenAI API. */
|
|
10
11
|
export interface AzureClientOptions extends ClientOptions {
|
package/src/client.ts
CHANGED
|
@@ -15,6 +15,9 @@ import { stringifyQuery } from './internal/utils/query';
|
|
|
15
15
|
import { VERSION } from './version';
|
|
16
16
|
import * as Errors from './core/error';
|
|
17
17
|
import * as Pagination from './core/pagination';
|
|
18
|
+
import type { WorkloadIdentity } from './auth/types';
|
|
19
|
+
import { WorkloadIdentityAuth } from './auth/workload-identity-auth';
|
|
20
|
+
import { OAuthError, SubjectTokenProviderError } from './core/error';
|
|
18
21
|
import {
|
|
19
22
|
AbstractPage,
|
|
20
23
|
type ConversationCursorPageParams,
|
|
@@ -238,6 +241,8 @@ import {
|
|
|
238
241
|
} from './internal/utils/log';
|
|
239
242
|
import { isEmptyObj } from './internal/utils/values';
|
|
240
243
|
|
|
244
|
+
const WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER = 'workload-identity-auth';
|
|
245
|
+
|
|
241
246
|
export type ApiKeySetter = () => Promise<string>;
|
|
242
247
|
|
|
243
248
|
export interface ClientOptions {
|
|
@@ -251,8 +256,10 @@ export interface ClientOptions {
|
|
|
251
256
|
* - The function must return a non-empty string; otherwise an OpenAIError is thrown.
|
|
252
257
|
* - If the function throws, the error is wrapped in an OpenAIError with the original
|
|
253
258
|
* error available as `cause`.
|
|
259
|
+
* - Mutually exclusive with `workloadIdentity`.
|
|
254
260
|
*/
|
|
255
261
|
apiKey?: string | ApiKeySetter | undefined;
|
|
262
|
+
|
|
256
263
|
/**
|
|
257
264
|
* Defaults to process.env['OPENAI_ORG_ID'].
|
|
258
265
|
*/
|
|
@@ -285,6 +292,7 @@ export interface ClientOptions {
|
|
|
285
292
|
* @unit milliseconds
|
|
286
293
|
*/
|
|
287
294
|
timeout?: number | undefined;
|
|
295
|
+
|
|
288
296
|
/**
|
|
289
297
|
* Additional `RequestInit` options to be passed to `fetch` calls.
|
|
290
298
|
* Properties will be overridden by per-request `fetchOptions`.
|
|
@@ -341,6 +349,12 @@ export interface ClientOptions {
|
|
|
341
349
|
* Defaults to globalThis.console.
|
|
342
350
|
*/
|
|
343
351
|
logger?: Logger | undefined;
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Workload identity configuration for OAuth2 token exchange authentication.
|
|
355
|
+
* Mutually exclusive with `apiKey`.
|
|
356
|
+
*/
|
|
357
|
+
workloadIdentity?: WorkloadIdentity | undefined;
|
|
344
358
|
}
|
|
345
359
|
|
|
346
360
|
/**
|
|
@@ -363,6 +377,7 @@ export class OpenAI {
|
|
|
363
377
|
#encoder: Opts.RequestEncoder;
|
|
364
378
|
protected idempotencyHeader?: string;
|
|
365
379
|
protected _options: ClientOptions;
|
|
380
|
+
private _workloadIdentityAuth?: WorkloadIdentityAuth;
|
|
366
381
|
|
|
367
382
|
/**
|
|
368
383
|
* API Client for interfacing with the OpenAI API.
|
|
@@ -386,11 +401,19 @@ export class OpenAI {
|
|
|
386
401
|
organization = readEnv('OPENAI_ORG_ID') ?? null,
|
|
387
402
|
project = readEnv('OPENAI_PROJECT_ID') ?? null,
|
|
388
403
|
webhookSecret = readEnv('OPENAI_WEBHOOK_SECRET') ?? null,
|
|
404
|
+
workloadIdentity,
|
|
389
405
|
...opts
|
|
390
406
|
}: ClientOptions = {}) {
|
|
391
|
-
if (
|
|
407
|
+
if (workloadIdentity) {
|
|
408
|
+
if (apiKey && apiKey !== WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER) {
|
|
409
|
+
throw new Errors.OpenAIError(
|
|
410
|
+
'The `apiKey` and `workloadIdentity` arguments are mutually exclusive; only one can be passed at a time.',
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
apiKey = WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER;
|
|
414
|
+
} else if (apiKey === undefined) {
|
|
392
415
|
throw new Errors.OpenAIError(
|
|
393
|
-
'Missing credentials. Please pass an `apiKey`, or set the `OPENAI_API_KEY` environment variable.',
|
|
416
|
+
'Missing credentials. Please pass an `apiKey`, `workloadIdentity`, or set the `OPENAI_API_KEY` environment variable.',
|
|
394
417
|
);
|
|
395
418
|
}
|
|
396
419
|
|
|
@@ -399,6 +422,7 @@ export class OpenAI {
|
|
|
399
422
|
organization,
|
|
400
423
|
project,
|
|
401
424
|
webhookSecret,
|
|
425
|
+
workloadIdentity,
|
|
402
426
|
...opts,
|
|
403
427
|
baseURL: baseURL || `https://api.openai.com/v1`,
|
|
404
428
|
};
|
|
@@ -426,6 +450,10 @@ export class OpenAI {
|
|
|
426
450
|
|
|
427
451
|
this._options = options;
|
|
428
452
|
|
|
453
|
+
if (workloadIdentity) {
|
|
454
|
+
this._workloadIdentityAuth = new WorkloadIdentityAuth(workloadIdentity, this.fetch);
|
|
455
|
+
}
|
|
456
|
+
|
|
429
457
|
this.apiKey = typeof apiKey === 'string' ? apiKey : 'Missing Key';
|
|
430
458
|
this.organization = organization;
|
|
431
459
|
this.project = project;
|
|
@@ -446,6 +474,7 @@ export class OpenAI {
|
|
|
446
474
|
fetch: this.fetch,
|
|
447
475
|
fetchOptions: this.fetchOptions,
|
|
448
476
|
apiKey: this.apiKey,
|
|
477
|
+
workloadIdentity: this._options.workloadIdentity,
|
|
449
478
|
organization: this.organization,
|
|
450
479
|
project: this.project,
|
|
451
480
|
webhookSecret: this.webhookSecret,
|
|
@@ -640,7 +669,7 @@ export class OpenAI {
|
|
|
640
669
|
}
|
|
641
670
|
|
|
642
671
|
const controller = new AbortController();
|
|
643
|
-
const response = await this.
|
|
672
|
+
const response = await this.fetchWithAuth(url, req, timeout, controller).catch(castToError);
|
|
644
673
|
const headersTime = Date.now();
|
|
645
674
|
|
|
646
675
|
if (response instanceof globalThis.Error) {
|
|
@@ -682,6 +711,9 @@ export class OpenAI {
|
|
|
682
711
|
message: response.message,
|
|
683
712
|
}),
|
|
684
713
|
);
|
|
714
|
+
if (response instanceof OAuthError || response instanceof SubjectTokenProviderError) {
|
|
715
|
+
throw response;
|
|
716
|
+
}
|
|
685
717
|
if (isTimeout) {
|
|
686
718
|
throw new Errors.APIConnectionTimeoutError();
|
|
687
719
|
}
|
|
@@ -697,6 +729,28 @@ export class OpenAI {
|
|
|
697
729
|
} with status ${response.status} in ${headersTime - startTime}ms`;
|
|
698
730
|
|
|
699
731
|
if (!response.ok) {
|
|
732
|
+
if (
|
|
733
|
+
response.status === 401 &&
|
|
734
|
+
this._workloadIdentityAuth &&
|
|
735
|
+
!options.__metadata?.['hasStreamingBody'] &&
|
|
736
|
+
!options.__metadata?.['workloadIdentityTokenRefreshed']
|
|
737
|
+
) {
|
|
738
|
+
await Shims.CancelReadableStream(response.body);
|
|
739
|
+
this._workloadIdentityAuth.invalidateToken();
|
|
740
|
+
|
|
741
|
+
return this.makeRequest(
|
|
742
|
+
{
|
|
743
|
+
...options,
|
|
744
|
+
__metadata: {
|
|
745
|
+
...options.__metadata,
|
|
746
|
+
workloadIdentityTokenRefreshed: true,
|
|
747
|
+
},
|
|
748
|
+
},
|
|
749
|
+
retriesRemaining,
|
|
750
|
+
retryOfRequestLogID ?? requestLogID,
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
|
|
700
754
|
const shouldRetry = await this.shouldRetry(response);
|
|
701
755
|
if (retriesRemaining && shouldRetry) {
|
|
702
756
|
const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;
|
|
@@ -785,6 +839,26 @@ export class OpenAI {
|
|
|
785
839
|
return new Pagination.PagePromise<PageClass, Item>(this as any as OpenAI, request, Page);
|
|
786
840
|
}
|
|
787
841
|
|
|
842
|
+
protected async fetchWithAuth(
|
|
843
|
+
url: RequestInfo,
|
|
844
|
+
init: RequestInit,
|
|
845
|
+
timeout: number,
|
|
846
|
+
controller: AbortController,
|
|
847
|
+
): Promise<Response> {
|
|
848
|
+
if (this._workloadIdentityAuth) {
|
|
849
|
+
const headers = init.headers as Headers;
|
|
850
|
+
const authHeader = headers.get('Authorization');
|
|
851
|
+
if (!authHeader || authHeader === `Bearer ${WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER}`) {
|
|
852
|
+
const token = await this._workloadIdentityAuth.getToken();
|
|
853
|
+
headers.set('Authorization', `Bearer ${token}`);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const response = await this.fetchWithTimeout(url, init, timeout, controller);
|
|
858
|
+
|
|
859
|
+
return response;
|
|
860
|
+
}
|
|
861
|
+
|
|
788
862
|
async fetchWithTimeout(
|
|
789
863
|
url: RequestInfo,
|
|
790
864
|
init: RequestInit | undefined,
|
|
@@ -908,7 +982,15 @@ export class OpenAI {
|
|
|
908
982
|
const url = this.buildURL(path!, query as Record<string, unknown>, defaultBaseURL);
|
|
909
983
|
if ('timeout' in options) validatePositiveInteger('timeout', options.timeout);
|
|
910
984
|
options.timeout = options.timeout ?? this.timeout;
|
|
911
|
-
const { bodyHeaders, body } = this.buildBody({ options });
|
|
985
|
+
const { bodyHeaders, body, isStreamingBody } = this.buildBody({ options });
|
|
986
|
+
|
|
987
|
+
if (isStreamingBody) {
|
|
988
|
+
inputOptions.__metadata = {
|
|
989
|
+
...inputOptions.__metadata,
|
|
990
|
+
hasStreamingBody: true,
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
|
|
912
994
|
const reqHeaders = await this.buildHeaders({ options: inputOptions, method, bodyHeaders, retryCount });
|
|
913
995
|
|
|
914
996
|
const req: FinalizedRequestInit = {
|
|
@@ -973,11 +1055,26 @@ export class OpenAI {
|
|
|
973
1055
|
private buildBody({ options: { body, headers: rawHeaders } }: { options: FinalRequestOptions }): {
|
|
974
1056
|
bodyHeaders: HeadersLike;
|
|
975
1057
|
body: BodyInit | undefined;
|
|
1058
|
+
isStreamingBody: boolean;
|
|
976
1059
|
} {
|
|
977
1060
|
if (!body) {
|
|
978
|
-
return { bodyHeaders: undefined, body: undefined };
|
|
1061
|
+
return { bodyHeaders: undefined, body: undefined, isStreamingBody: false };
|
|
979
1062
|
}
|
|
980
1063
|
const headers = buildHeaders([rawHeaders]);
|
|
1064
|
+
|
|
1065
|
+
const isReadableStream =
|
|
1066
|
+
typeof (globalThis as any).ReadableStream !== 'undefined' &&
|
|
1067
|
+
body instanceof (globalThis as any).ReadableStream;
|
|
1068
|
+
|
|
1069
|
+
const isRetryableBody =
|
|
1070
|
+
!isReadableStream &&
|
|
1071
|
+
(typeof body === 'string' ||
|
|
1072
|
+
body instanceof ArrayBuffer ||
|
|
1073
|
+
ArrayBuffer.isView(body) ||
|
|
1074
|
+
(typeof (globalThis as any).Blob !== 'undefined' && body instanceof (globalThis as any).Blob) ||
|
|
1075
|
+
body instanceof URLSearchParams ||
|
|
1076
|
+
body instanceof FormData);
|
|
1077
|
+
|
|
981
1078
|
if (
|
|
982
1079
|
// Pass raw type verbatim
|
|
983
1080
|
ArrayBuffer.isView(body) ||
|
|
@@ -993,15 +1090,19 @@ export class OpenAI {
|
|
|
993
1090
|
// `URLSearchParams` -> `application/x-www-form-urlencoded`
|
|
994
1091
|
body instanceof URLSearchParams ||
|
|
995
1092
|
// Send chunked stream (each chunk has own `length`)
|
|
996
|
-
|
|
1093
|
+
isReadableStream
|
|
997
1094
|
) {
|
|
998
|
-
return { bodyHeaders: undefined, body: body as BodyInit };
|
|
1095
|
+
return { bodyHeaders: undefined, body: body as BodyInit, isStreamingBody: !isRetryableBody };
|
|
999
1096
|
} else if (
|
|
1000
1097
|
typeof body === 'object' &&
|
|
1001
1098
|
(Symbol.asyncIterator in body ||
|
|
1002
1099
|
(Symbol.iterator in body && 'next' in body && typeof body.next === 'function'))
|
|
1003
1100
|
) {
|
|
1004
|
-
return {
|
|
1101
|
+
return {
|
|
1102
|
+
bodyHeaders: undefined,
|
|
1103
|
+
body: Shims.ReadableStreamFrom(body as AsyncIterable<Uint8Array>),
|
|
1104
|
+
isStreamingBody: true,
|
|
1105
|
+
};
|
|
1005
1106
|
} else if (
|
|
1006
1107
|
typeof body === 'object' &&
|
|
1007
1108
|
headers.values.get('content-type') === 'application/x-www-form-urlencoded'
|
|
@@ -1009,9 +1110,10 @@ export class OpenAI {
|
|
|
1009
1110
|
return {
|
|
1010
1111
|
bodyHeaders: { 'content-type': 'application/x-www-form-urlencoded' },
|
|
1011
1112
|
body: this.stringifyQuery(body),
|
|
1113
|
+
isStreamingBody: false,
|
|
1012
1114
|
};
|
|
1013
1115
|
} else {
|
|
1014
|
-
return this.#encoder({ body, headers });
|
|
1116
|
+
return { ...this.#encoder({ body, headers }), isStreamingBody: false };
|
|
1015
1117
|
}
|
|
1016
1118
|
}
|
|
1017
1119
|
|
|
@@ -1361,6 +1463,7 @@ export declare namespace OpenAI {
|
|
|
1361
1463
|
export type FunctionDefinition = API.FunctionDefinition;
|
|
1362
1464
|
export type FunctionParameters = API.FunctionParameters;
|
|
1363
1465
|
export type Metadata = API.Metadata;
|
|
1466
|
+
export type OAuthErrorCode = API.OAuthErrorCode;
|
|
1364
1467
|
export type Reasoning = API.Reasoning;
|
|
1365
1468
|
export type ReasoningEffort = API.ReasoningEffort;
|
|
1366
1469
|
export type ResponseFormatJSONObject = API.ResponseFormatJSONObject;
|
package/src/core/error.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
|
2
2
|
|
|
3
3
|
import { castToError } from '../internal/errors';
|
|
4
|
+
import type { OAuthErrorCode } from '../resources/shared';
|
|
4
5
|
|
|
5
6
|
export class OpenAIError extends Error {}
|
|
6
7
|
|
|
@@ -158,3 +159,42 @@ export class InvalidWebhookSignatureError extends Error {
|
|
|
158
159
|
super(message);
|
|
159
160
|
}
|
|
160
161
|
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Error thrown by the API server during OAuth token exchange.
|
|
165
|
+
* Can have status codes 400, 401, or 403.
|
|
166
|
+
* Other status codes from OAuth endpoints are raised as normal APIError types.
|
|
167
|
+
*/
|
|
168
|
+
export class OAuthError extends APIError<400 | 401 | 403, Headers> {
|
|
169
|
+
readonly error_code: OAuthErrorCode | undefined;
|
|
170
|
+
|
|
171
|
+
constructor(status: 400 | 401 | 403, error: Object | undefined, headers: Headers) {
|
|
172
|
+
let finalMessage = 'OAuth2 authentication error';
|
|
173
|
+
let error_code: OAuthErrorCode | undefined = undefined;
|
|
174
|
+
|
|
175
|
+
if (error && typeof error === 'object') {
|
|
176
|
+
const errorData = error as Record<string, any>;
|
|
177
|
+
error_code = errorData['error'];
|
|
178
|
+
const description = errorData['error_description'];
|
|
179
|
+
if (description && typeof description === 'string') {
|
|
180
|
+
finalMessage = description;
|
|
181
|
+
} else if (error_code) {
|
|
182
|
+
finalMessage = error_code;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
super(status, error, finalMessage, headers);
|
|
187
|
+
this.error_code = error_code;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export class SubjectTokenProviderError extends OpenAIError {
|
|
192
|
+
readonly provider: string;
|
|
193
|
+
readonly cause: Error | undefined;
|
|
194
|
+
|
|
195
|
+
constructor(message: string, provider: string, cause?: Error) {
|
|
196
|
+
super(message);
|
|
197
|
+
this.provider = provider;
|
|
198
|
+
this.cause = cause;
|
|
199
|
+
}
|
|
200
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -170,6 +170,14 @@ export interface Message {
|
|
|
170
170
|
* The type of the message. Always set to `message`.
|
|
171
171
|
*/
|
|
172
172
|
type: 'message';
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Labels an `assistant` message as intermediate commentary (`commentary`) or the
|
|
176
|
+
* final answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when
|
|
177
|
+
* sending follow-up requests, preserve and resend phase on all assistant messages
|
|
178
|
+
* — dropping it can degrade performance. Not used for user messages.
|
|
179
|
+
*/
|
|
180
|
+
phase?: 'commentary' | 'final_answer' | null;
|
|
173
181
|
}
|
|
174
182
|
|
|
175
183
|
export namespace Message {
|
|
@@ -91,6 +91,7 @@ export type ConversationItem =
|
|
|
91
91
|
| ResponsesAPI.ResponseToolSearchCall
|
|
92
92
|
| ResponsesAPI.ResponseToolSearchOutputItem
|
|
93
93
|
| ResponsesAPI.ResponseReasoningItem
|
|
94
|
+
| ResponsesAPI.ResponseCompactionItem
|
|
94
95
|
| ResponsesAPI.ResponseCodeInterpreterToolCall
|
|
95
96
|
| ConversationItem.LocalShellCall
|
|
96
97
|
| ConversationItem.LocalShellCallOutput
|
|
@@ -172,8 +172,9 @@ export interface CallAcceptParams {
|
|
|
172
172
|
|
|
173
173
|
/**
|
|
174
174
|
* Realtime API can write session traces to the
|
|
175
|
-
* [Traces Dashboard](/logs?api=traces). Set to null to
|
|
176
|
-
* tracing is enabled for a session, the configuration cannot
|
|
175
|
+
* [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to
|
|
176
|
+
* disable tracing. Once tracing is enabled for a session, the configuration cannot
|
|
177
|
+
* be modified.
|
|
177
178
|
*
|
|
178
179
|
* `auto` will create a trace for the session with default values for the workflow
|
|
179
180
|
* name, group id, and metadata.
|
|
@@ -153,8 +153,9 @@ export interface RealtimeSessionCreateResponse {
|
|
|
153
153
|
|
|
154
154
|
/**
|
|
155
155
|
* Realtime API can write session traces to the
|
|
156
|
-
* [Traces Dashboard](/logs?api=traces). Set to null to
|
|
157
|
-
* tracing is enabled for a session, the configuration cannot
|
|
156
|
+
* [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to
|
|
157
|
+
* disable tracing. Once tracing is enabled for a session, the configuration cannot
|
|
158
|
+
* be modified.
|
|
158
159
|
*
|
|
159
160
|
* `auto` will create a trace for the session with default values for the workflow
|
|
160
161
|
* name, group id, and metadata.
|
|
@@ -3136,8 +3136,9 @@ export interface RealtimeSessionCreateRequest {
|
|
|
3136
3136
|
|
|
3137
3137
|
/**
|
|
3138
3138
|
* Realtime API can write session traces to the
|
|
3139
|
-
* [Traces Dashboard](/logs?api=traces). Set to null to
|
|
3140
|
-
* tracing is enabled for a session, the configuration cannot
|
|
3139
|
+
* [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to
|
|
3140
|
+
* disable tracing. Once tracing is enabled for a session, the configuration cannot
|
|
3141
|
+
* be modified.
|
|
3141
3142
|
*
|
|
3142
3143
|
* `auto` will create a trace for the session with default values for the workflow
|
|
3143
3144
|
* name, group id, and metadata.
|
|
@@ -3349,8 +3350,9 @@ export namespace RealtimeToolsConfigUnion {
|
|
|
3349
3350
|
|
|
3350
3351
|
/**
|
|
3351
3352
|
* Realtime API can write session traces to the
|
|
3352
|
-
* [Traces Dashboard](/logs?api=traces). Set to null to
|
|
3353
|
-
* tracing is enabled for a session, the configuration cannot
|
|
3353
|
+
* [Traces Dashboard](https://platform.openai.com/logs?api=traces). Set to null to
|
|
3354
|
+
* disable tracing. Once tracing is enabled for a session, the configuration cannot
|
|
3355
|
+
* be modified.
|
|
3354
3356
|
*
|
|
3355
3357
|
* `auto` will create a trace for the session with default values for the workflow
|
|
3356
3358
|
* name, group id, and metadata.
|
|
@@ -51,7 +51,9 @@ Types:
|
|
|
51
51
|
- <code><a href="./src/resources/responses/responses.ts">ResponseCustomToolCall</a></code>
|
|
52
52
|
- <code><a href="./src/resources/responses/responses.ts">ResponseCustomToolCallInputDeltaEvent</a></code>
|
|
53
53
|
- <code><a href="./src/resources/responses/responses.ts">ResponseCustomToolCallInputDoneEvent</a></code>
|
|
54
|
+
- <code><a href="./src/resources/responses/responses.ts">ResponseCustomToolCallItem</a></code>
|
|
54
55
|
- <code><a href="./src/resources/responses/responses.ts">ResponseCustomToolCallOutput</a></code>
|
|
56
|
+
- <code><a href="./src/resources/responses/responses.ts">ResponseCustomToolCallOutputItem</a></code>
|
|
55
57
|
- <code><a href="./src/resources/responses/responses.ts">ResponseError</a></code>
|
|
56
58
|
- <code><a href="./src/resources/responses/responses.ts">ResponseErrorEvent</a></code>
|
|
57
59
|
- <code><a href="./src/resources/responses/responses.ts">ResponseFailedEvent</a></code>
|
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import * as ResponsesAPI from './responses';
|
|
4
4
|
import { OpenAI } from '../../client';
|
|
5
|
-
|
|
6
5
|
import { EventEmitter } from '../../core/EventEmitter';
|
|
7
6
|
import { OpenAIError } from '../../core/error';
|
|
8
7
|
import { stringifyQuery } from '../../internal/utils';
|
|
9
8
|
|
|
9
|
+
export type ResponsesStreamMessage =
|
|
10
|
+
| { type: 'connecting' | 'open' | 'closing' | 'close' }
|
|
11
|
+
| { type: 'message'; message: ResponsesAPI.ResponsesServerEvent }
|
|
12
|
+
| { type: 'error'; error: WebSocketError };
|
|
13
|
+
|
|
10
14
|
export class WebSocketError extends OpenAIError {
|
|
11
15
|
/**
|
|
12
16
|
* The error data that the API sent back in an error event.
|