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.
Files changed (146) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +96 -0
  3. package/auth/index.d.mts +4 -0
  4. package/auth/index.d.mts.map +1 -0
  5. package/auth/index.d.ts +4 -0
  6. package/auth/index.d.ts.map +1 -0
  7. package/auth/index.js +11 -0
  8. package/auth/index.js.map +1 -0
  9. package/auth/index.mjs +3 -0
  10. package/auth/index.mjs.map +1 -0
  11. package/auth/subject-token-providers.d.mts +20 -0
  12. package/auth/subject-token-providers.d.mts.map +1 -0
  13. package/auth/subject-token-providers.d.ts +20 -0
  14. package/auth/subject-token-providers.d.ts.map +1 -0
  15. package/auth/subject-token-providers.js +127 -0
  16. package/auth/subject-token-providers.js.map +1 -0
  17. package/auth/subject-token-providers.mjs +121 -0
  18. package/auth/subject-token-providers.mjs.map +1 -0
  19. package/auth/types.d.mts +23 -0
  20. package/auth/types.d.mts.map +1 -0
  21. package/auth/types.d.ts +23 -0
  22. package/auth/types.d.ts.map +1 -0
  23. package/auth/types.js +3 -0
  24. package/auth/types.js.map +1 -0
  25. package/auth/types.mjs +2 -0
  26. package/auth/types.mjs.map +1 -0
  27. package/auth/workload-identity-auth.d.mts +16 -0
  28. package/auth/workload-identity-auth.d.mts.map +1 -0
  29. package/auth/workload-identity-auth.d.ts +16 -0
  30. package/auth/workload-identity-auth.d.ts.map +1 -0
  31. package/auth/workload-identity-auth.js +92 -0
  32. package/auth/workload-identity-auth.js.map +1 -0
  33. package/auth/workload-identity-auth.mjs +87 -0
  34. package/auth/workload-identity-auth.mjs.map +1 -0
  35. package/azure.d.mts +2 -1
  36. package/azure.d.mts.map +1 -1
  37. package/azure.d.ts +2 -1
  38. package/azure.d.ts.map +1 -1
  39. package/azure.js.map +1 -1
  40. package/azure.mjs.map +1 -1
  41. package/client.d.mts +11 -1
  42. package/client.d.mts.map +1 -1
  43. package/client.d.ts +11 -1
  44. package/client.d.ts.map +1 -1
  45. package/client.js +73 -10
  46. package/client.js.map +1 -1
  47. package/client.mjs +73 -10
  48. package/client.mjs.map +1 -1
  49. package/core/error.d.mts +15 -0
  50. package/core/error.d.mts.map +1 -1
  51. package/core/error.d.ts +15 -0
  52. package/core/error.d.ts.map +1 -1
  53. package/core/error.js +34 -1
  54. package/core/error.js.map +1 -1
  55. package/core/error.mjs +31 -0
  56. package/core/error.mjs.map +1 -1
  57. package/index.d.mts +1 -1
  58. package/index.d.mts.map +1 -1
  59. package/index.d.ts +1 -1
  60. package/index.d.ts.map +1 -1
  61. package/index.js +3 -1
  62. package/index.js.map +1 -1
  63. package/index.mjs +1 -1
  64. package/index.mjs.map +1 -1
  65. package/package.json +11 -1
  66. package/resources/conversations/conversations.d.mts +7 -0
  67. package/resources/conversations/conversations.d.mts.map +1 -1
  68. package/resources/conversations/conversations.d.ts +7 -0
  69. package/resources/conversations/conversations.d.ts.map +1 -1
  70. package/resources/conversations/conversations.js.map +1 -1
  71. package/resources/conversations/conversations.mjs.map +1 -1
  72. package/resources/conversations/items.d.mts +1 -1
  73. package/resources/conversations/items.d.mts.map +1 -1
  74. package/resources/conversations/items.d.ts +1 -1
  75. package/resources/conversations/items.d.ts.map +1 -1
  76. package/resources/realtime/calls.d.mts +3 -2
  77. package/resources/realtime/calls.d.mts.map +1 -1
  78. package/resources/realtime/calls.d.ts +3 -2
  79. package/resources/realtime/calls.d.ts.map +1 -1
  80. package/resources/realtime/client-secrets.d.mts +3 -2
  81. package/resources/realtime/client-secrets.d.mts.map +1 -1
  82. package/resources/realtime/client-secrets.d.ts +3 -2
  83. package/resources/realtime/client-secrets.d.ts.map +1 -1
  84. package/resources/realtime/realtime.d.mts +6 -4
  85. package/resources/realtime/realtime.d.mts.map +1 -1
  86. package/resources/realtime/realtime.d.ts +6 -4
  87. package/resources/realtime/realtime.d.ts.map +1 -1
  88. package/resources/realtime/realtime.js.map +1 -1
  89. package/resources/realtime/realtime.mjs.map +1 -1
  90. package/resources/responses/internal-base.d.mts +9 -0
  91. package/resources/responses/internal-base.d.mts.map +1 -1
  92. package/resources/responses/internal-base.d.ts +9 -0
  93. package/resources/responses/internal-base.d.ts.map +1 -1
  94. package/resources/responses/internal-base.js.map +1 -1
  95. package/resources/responses/internal-base.mjs.map +1 -1
  96. package/resources/responses/responses.d.mts +153 -14
  97. package/resources/responses/responses.d.mts.map +1 -1
  98. package/resources/responses/responses.d.ts +153 -14
  99. package/resources/responses/responses.d.ts.map +1 -1
  100. package/resources/responses/responses.js.map +1 -1
  101. package/resources/responses/responses.mjs.map +1 -1
  102. package/resources/responses/ws.d.mts +26 -1
  103. package/resources/responses/ws.d.mts.map +1 -1
  104. package/resources/responses/ws.d.ts +26 -1
  105. package/resources/responses/ws.d.ts.map +1 -1
  106. package/resources/responses/ws.js +118 -0
  107. package/resources/responses/ws.js.map +1 -1
  108. package/resources/responses/ws.mjs +118 -0
  109. package/resources/responses/ws.mjs.map +1 -1
  110. package/resources/shared.d.mts +1 -0
  111. package/resources/shared.d.mts.map +1 -1
  112. package/resources/shared.d.ts +1 -0
  113. package/resources/shared.d.ts.map +1 -1
  114. package/resources/vector-stores/file-batches.d.mts +9 -5
  115. package/resources/vector-stores/file-batches.d.mts.map +1 -1
  116. package/resources/vector-stores/file-batches.d.ts +9 -5
  117. package/resources/vector-stores/file-batches.d.ts.map +1 -1
  118. package/resources/vector-stores/files.d.mts +3 -1
  119. package/resources/vector-stores/files.d.mts.map +1 -1
  120. package/resources/vector-stores/files.d.ts +3 -1
  121. package/resources/vector-stores/files.d.ts.map +1 -1
  122. package/src/auth/index.ts +9 -0
  123. package/src/auth/subject-token-providers.ts +185 -0
  124. package/src/auth/types.ts +28 -0
  125. package/src/auth/workload-identity-auth.ts +118 -0
  126. package/src/azure.ts +2 -1
  127. package/src/client.ts +112 -9
  128. package/src/core/error.ts +40 -0
  129. package/src/index.ts +2 -0
  130. package/src/resources/conversations/conversations.ts +8 -0
  131. package/src/resources/conversations/items.ts +1 -0
  132. package/src/resources/realtime/calls.ts +3 -2
  133. package/src/resources/realtime/client-secrets.ts +3 -2
  134. package/src/resources/realtime/realtime.ts +6 -4
  135. package/src/resources/responses/api.md +2 -0
  136. package/src/resources/responses/internal-base.ts +5 -1
  137. package/src/resources/responses/responses.ts +198 -14
  138. package/src/resources/responses/ws.ts +130 -1
  139. package/src/resources/shared.ts +2 -0
  140. package/src/resources/vector-stores/file-batches.ts +9 -5
  141. package/src/resources/vector-stores/files.ts +3 -1
  142. package/src/version.ts +1 -1
  143. package/version.d.mts +1 -1
  144. package/version.d.ts +1 -1
  145. package/version.js +1 -1
  146. 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 { ClientOptions, OpenAI } from './client';
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 (apiKey === undefined) {
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.fetchWithTimeout(url, req, timeout, controller).catch(castToError);
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
- ((globalThis as any).ReadableStream && body instanceof (globalThis as any).ReadableStream)
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 { bodyHeaders: undefined, body: Shims.ReadableStreamFrom(body as AsyncIterable<Uint8Array>) };
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
@@ -21,6 +21,8 @@ export {
21
21
  PermissionDeniedError,
22
22
  UnprocessableEntityError,
23
23
  InvalidWebhookSignatureError,
24
+ OAuthError,
25
+ SubjectTokenProviderError,
24
26
  } from './core/error';
25
27
 
26
28
  export { AzureOpenAI } from './azure';
@@ -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 disable tracing. Once
176
- * tracing is enabled for a session, the configuration cannot be modified.
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 disable tracing. Once
157
- * tracing is enabled for a session, the configuration cannot be modified.
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 disable tracing. Once
3140
- * tracing is enabled for a session, the configuration cannot be modified.
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 disable tracing. Once
3353
- * tracing is enabled for a session, the configuration cannot be modified.
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.