openai 6.46.0 → 6.48.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 (236) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +9 -2
  3. package/client.d.mts +1 -0
  4. package/client.d.mts.map +1 -1
  5. package/client.d.ts +1 -0
  6. package/client.d.ts.map +1 -1
  7. package/client.js +11 -5
  8. package/client.js.map +1 -1
  9. package/client.mjs +11 -5
  10. package/client.mjs.map +1 -1
  11. package/core/streaming.d.mts.map +1 -1
  12. package/core/streaming.d.ts.map +1 -1
  13. package/core/streaming.js +38 -6
  14. package/core/streaming.js.map +1 -1
  15. package/core/streaming.mjs +38 -6
  16. package/core/streaming.mjs.map +1 -1
  17. package/core/uploads.d.mts +1 -1
  18. package/core/uploads.d.mts.map +1 -1
  19. package/core/uploads.d.ts +1 -1
  20. package/core/uploads.d.ts.map +1 -1
  21. package/core/uploads.js +3 -1
  22. package/core/uploads.js.map +1 -1
  23. package/core/uploads.mjs +1 -0
  24. package/core/uploads.mjs.map +1 -1
  25. package/helpers/zod.d.mts +20 -7
  26. package/helpers/zod.d.mts.map +1 -1
  27. package/helpers/zod.d.ts +20 -7
  28. package/helpers/zod.d.ts.map +1 -1
  29. package/helpers/zod.js +24 -12
  30. package/helpers/zod.js.map +1 -1
  31. package/helpers/zod.mjs +24 -12
  32. package/helpers/zod.mjs.map +1 -1
  33. package/index.d.mts +1 -1
  34. package/index.d.mts.map +1 -1
  35. package/index.d.ts +1 -1
  36. package/index.d.ts.map +1 -1
  37. package/index.js +2 -1
  38. package/index.js.map +1 -1
  39. package/index.mjs +1 -1
  40. package/index.mjs.map +1 -1
  41. package/internal/headers.d.mts.map +1 -1
  42. package/internal/headers.d.ts.map +1 -1
  43. package/internal/headers.js +7 -3
  44. package/internal/headers.js.map +1 -1
  45. package/internal/headers.mjs +7 -3
  46. package/internal/headers.mjs.map +1 -1
  47. package/internal/uploads.d.mts +26 -1
  48. package/internal/uploads.d.mts.map +1 -1
  49. package/internal/uploads.d.ts +26 -1
  50. package/internal/uploads.d.ts.map +1 -1
  51. package/internal/uploads.js +169 -1
  52. package/internal/uploads.js.map +1 -1
  53. package/internal/uploads.mjs +169 -2
  54. package/internal/uploads.mjs.map +1 -1
  55. package/lib/AbstractChatCompletionRunner.d.mts +3 -3
  56. package/lib/AbstractChatCompletionRunner.d.mts.map +1 -1
  57. package/lib/AbstractChatCompletionRunner.d.ts +3 -3
  58. package/lib/AbstractChatCompletionRunner.d.ts.map +1 -1
  59. package/lib/AbstractChatCompletionRunner.js +4 -3
  60. package/lib/AbstractChatCompletionRunner.js.map +1 -1
  61. package/lib/AbstractChatCompletionRunner.mjs +4 -3
  62. package/lib/AbstractChatCompletionRunner.mjs.map +1 -1
  63. package/lib/AssistantStream.d.mts.map +1 -1
  64. package/lib/AssistantStream.d.ts.map +1 -1
  65. package/lib/AssistantStream.js +3 -2
  66. package/lib/AssistantStream.js.map +1 -1
  67. package/lib/AssistantStream.mjs +3 -2
  68. package/lib/AssistantStream.mjs.map +1 -1
  69. package/lib/ChatCompletionRunner.d.mts +26 -2
  70. package/lib/ChatCompletionRunner.d.mts.map +1 -1
  71. package/lib/ChatCompletionRunner.d.ts +26 -2
  72. package/lib/ChatCompletionRunner.d.ts.map +1 -1
  73. package/lib/ChatCompletionRunner.js.map +1 -1
  74. package/lib/ChatCompletionRunner.mjs.map +1 -1
  75. package/lib/ChatCompletionStream.d.mts +14 -1
  76. package/lib/ChatCompletionStream.d.mts.map +1 -1
  77. package/lib/ChatCompletionStream.d.ts +14 -1
  78. package/lib/ChatCompletionStream.d.ts.map +1 -1
  79. package/lib/ChatCompletionStream.js +68 -12
  80. package/lib/ChatCompletionStream.js.map +1 -1
  81. package/lib/ChatCompletionStream.mjs +67 -12
  82. package/lib/ChatCompletionStream.mjs.map +1 -1
  83. package/lib/ChatCompletionStreamingRunner.d.mts +25 -2
  84. package/lib/ChatCompletionStreamingRunner.d.mts.map +1 -1
  85. package/lib/ChatCompletionStreamingRunner.d.ts +25 -2
  86. package/lib/ChatCompletionStreamingRunner.d.ts.map +1 -1
  87. package/lib/ChatCompletionStreamingRunner.js +77 -0
  88. package/lib/ChatCompletionStreamingRunner.js.map +1 -1
  89. package/lib/ChatCompletionStreamingRunner.mjs +78 -1
  90. package/lib/ChatCompletionStreamingRunner.mjs.map +1 -1
  91. package/lib/EventStream.d.mts +13 -0
  92. package/lib/EventStream.d.mts.map +1 -1
  93. package/lib/EventStream.d.ts +13 -0
  94. package/lib/EventStream.d.ts.map +1 -1
  95. package/lib/EventStream.js +107 -2
  96. package/lib/EventStream.js.map +1 -1
  97. package/lib/EventStream.mjs +107 -2
  98. package/lib/EventStream.mjs.map +1 -1
  99. package/lib/RunnableFunction.d.mts +38 -21
  100. package/lib/RunnableFunction.d.mts.map +1 -1
  101. package/lib/RunnableFunction.d.ts +38 -21
  102. package/lib/RunnableFunction.d.ts.map +1 -1
  103. package/lib/RunnableFunction.js.map +1 -1
  104. package/lib/RunnableFunction.mjs.map +1 -1
  105. package/lib/parser.d.mts +2 -2
  106. package/lib/parser.d.mts.map +1 -1
  107. package/lib/parser.d.ts +2 -2
  108. package/lib/parser.d.ts.map +1 -1
  109. package/lib/parser.js.map +1 -1
  110. package/lib/parser.mjs.map +1 -1
  111. package/lib/responses/ResponseStream.d.mts +6 -1
  112. package/lib/responses/ResponseStream.d.mts.map +1 -1
  113. package/lib/responses/ResponseStream.d.ts +6 -1
  114. package/lib/responses/ResponseStream.d.ts.map +1 -1
  115. package/lib/responses/ResponseStream.js +21 -0
  116. package/lib/responses/ResponseStream.js.map +1 -1
  117. package/lib/responses/ResponseStream.mjs +21 -0
  118. package/lib/responses/ResponseStream.mjs.map +1 -1
  119. package/package.json +1 -1
  120. package/resources/admin/organization/audit-logs.d.mts +2 -2
  121. package/resources/admin/organization/audit-logs.d.mts.map +1 -1
  122. package/resources/admin/organization/audit-logs.d.ts +2 -2
  123. package/resources/admin/organization/audit-logs.d.ts.map +1 -1
  124. package/resources/admin/organization/projects/api-keys.d.mts +12 -0
  125. package/resources/admin/organization/projects/api-keys.d.mts.map +1 -1
  126. package/resources/admin/organization/projects/api-keys.d.ts +12 -0
  127. package/resources/admin/organization/projects/api-keys.d.ts.map +1 -1
  128. package/resources/admin/organization/projects/index.d.mts +1 -1
  129. package/resources/admin/organization/projects/index.d.mts.map +1 -1
  130. package/resources/admin/organization/projects/index.d.ts +1 -1
  131. package/resources/admin/organization/projects/index.d.ts.map +1 -1
  132. package/resources/admin/organization/projects/index.js +4 -4
  133. package/resources/admin/organization/projects/index.js.map +1 -1
  134. package/resources/admin/organization/projects/index.mjs +1 -1
  135. package/resources/admin/organization/projects/index.mjs.map +1 -1
  136. package/resources/admin/organization/projects/projects.d.mts +2 -2
  137. package/resources/admin/organization/projects/projects.d.mts.map +1 -1
  138. package/resources/admin/organization/projects/projects.d.ts +2 -2
  139. package/resources/admin/organization/projects/projects.d.ts.map +1 -1
  140. package/resources/admin/organization/projects/projects.js +2 -2
  141. package/resources/admin/organization/projects/projects.js.map +1 -1
  142. package/resources/admin/organization/projects/projects.mjs +2 -2
  143. package/resources/admin/organization/projects/projects.mjs.map +1 -1
  144. package/resources/admin/organization/projects/service-accounts/api-keys.d.mts +58 -0
  145. package/resources/admin/organization/projects/service-accounts/api-keys.d.mts.map +1 -0
  146. package/resources/admin/organization/projects/service-accounts/api-keys.d.ts +58 -0
  147. package/resources/admin/organization/projects/service-accounts/api-keys.d.ts.map +1 -0
  148. package/resources/admin/organization/projects/service-accounts/api-keys.js +26 -0
  149. package/resources/admin/organization/projects/service-accounts/api-keys.js.map +1 -0
  150. package/resources/admin/organization/projects/service-accounts/api-keys.mjs +22 -0
  151. package/resources/admin/organization/projects/service-accounts/api-keys.mjs.map +1 -0
  152. package/resources/admin/organization/projects/service-accounts/index.d.mts +3 -0
  153. package/resources/admin/organization/projects/service-accounts/index.d.mts.map +1 -0
  154. package/resources/admin/organization/projects/service-accounts/index.d.ts +3 -0
  155. package/resources/admin/organization/projects/service-accounts/index.d.ts.map +1 -0
  156. package/resources/admin/organization/projects/service-accounts/index.js +9 -0
  157. package/resources/admin/organization/projects/service-accounts/index.js.map +1 -0
  158. package/resources/admin/organization/projects/service-accounts/index.mjs +4 -0
  159. package/resources/admin/organization/projects/service-accounts/index.mjs.map +1 -0
  160. package/resources/admin/organization/projects/service-accounts/service-accounts.d.mts +177 -0
  161. package/resources/admin/organization/projects/service-accounts/service-accounts.d.mts.map +1 -0
  162. package/resources/admin/organization/projects/service-accounts/service-accounts.d.ts +177 -0
  163. package/resources/admin/organization/projects/service-accounts/service-accounts.d.ts.map +1 -0
  164. package/resources/admin/organization/projects/service-accounts/service-accounts.js +109 -0
  165. package/resources/admin/organization/projects/service-accounts/service-accounts.js.map +1 -0
  166. package/resources/admin/organization/projects/service-accounts/service-accounts.mjs +104 -0
  167. package/resources/admin/organization/projects/service-accounts/service-accounts.mjs.map +1 -0
  168. package/resources/admin/organization/projects/service-accounts.d.mts +1 -167
  169. package/resources/admin/organization/projects/service-accounts.d.mts.map +1 -1
  170. package/resources/admin/organization/projects/service-accounts.d.ts +1 -167
  171. package/resources/admin/organization/projects/service-accounts.d.ts.map +1 -1
  172. package/resources/admin/organization/projects/service-accounts.js +2 -97
  173. package/resources/admin/organization/projects/service-accounts.js.map +1 -1
  174. package/resources/admin/organization/projects/service-accounts.mjs +1 -95
  175. package/resources/admin/organization/projects/service-accounts.mjs.map +1 -1
  176. package/resources/admin/organization/usage.d.mts +517 -88
  177. package/resources/admin/organization/usage.d.mts.map +1 -1
  178. package/resources/admin/organization/usage.d.ts +517 -88
  179. package/resources/admin/organization/usage.d.ts.map +1 -1
  180. package/resources/beta/responses/responses.d.mts +7 -6
  181. package/resources/beta/responses/responses.d.mts.map +1 -1
  182. package/resources/beta/responses/responses.d.ts +7 -6
  183. package/resources/beta/responses/responses.d.ts.map +1 -1
  184. package/resources/beta/responses/responses.js.map +1 -1
  185. package/resources/beta/responses/responses.mjs.map +1 -1
  186. package/resources/chat/completions/completions.d.mts +15 -6
  187. package/resources/chat/completions/completions.d.mts.map +1 -1
  188. package/resources/chat/completions/completions.d.ts +15 -6
  189. package/resources/chat/completions/completions.d.ts.map +1 -1
  190. package/resources/chat/completions/completions.js.map +1 -1
  191. package/resources/chat/completions/completions.mjs.map +1 -1
  192. package/resources/responses/responses.d.mts +3 -2
  193. package/resources/responses/responses.d.mts.map +1 -1
  194. package/resources/responses/responses.d.ts +3 -2
  195. package/resources/responses/responses.d.ts.map +1 -1
  196. package/resources/responses/responses.js.map +1 -1
  197. package/resources/responses/responses.mjs.map +1 -1
  198. package/resources/webhooks/webhooks.d.mts +2 -54
  199. package/resources/webhooks/webhooks.d.mts.map +1 -1
  200. package/resources/webhooks/webhooks.d.ts +2 -54
  201. package/resources/webhooks/webhooks.d.ts.map +1 -1
  202. package/src/client.ts +13 -5
  203. package/src/core/streaming.ts +36 -8
  204. package/src/core/uploads.ts +6 -1
  205. package/src/helpers/zod.ts +62 -23
  206. package/src/index.ts +1 -1
  207. package/src/internal/headers.ts +7 -3
  208. package/src/internal/uploads.ts +214 -4
  209. package/src/lib/AbstractChatCompletionRunner.ts +17 -9
  210. package/src/lib/AssistantStream.ts +3 -2
  211. package/src/lib/ChatCompletionRunner.ts +46 -6
  212. package/src/lib/ChatCompletionStream.ts +108 -13
  213. package/src/lib/ChatCompletionStreamingRunner.ts +144 -7
  214. package/src/lib/EventStream.ts +114 -4
  215. package/src/lib/RunnableFunction.ts +60 -31
  216. package/src/lib/parser.ts +4 -0
  217. package/src/lib/responses/ResponseStream.ts +29 -1
  218. package/src/resources/admin/organization/audit-logs.ts +169 -1
  219. package/src/resources/admin/organization/projects/api-keys.ts +15 -1
  220. package/src/resources/admin/organization/projects/index.ts +1 -1
  221. package/src/resources/admin/organization/projects/projects.ts +13 -13
  222. package/src/resources/admin/organization/projects/service-accounts/api-keys.ts +80 -0
  223. package/src/resources/admin/organization/projects/service-accounts/index.ts +15 -0
  224. package/src/resources/admin/organization/projects/service-accounts/service-accounts.ts +284 -0
  225. package/src/resources/admin/organization/projects/service-accounts.ts +1 -264
  226. package/src/resources/admin/organization/usage.ts +616 -88
  227. package/src/resources/beta/responses/responses.ts +7 -6
  228. package/src/resources/chat/completions/completions.ts +66 -9
  229. package/src/resources/responses/responses.ts +3 -2
  230. package/src/resources/webhooks/api.md +0 -1
  231. package/src/resources/webhooks/webhooks.ts +1 -65
  232. package/src/version.ts +1 -1
  233. package/version.d.mts +1 -1
  234. package/version.d.ts +1 -1
  235. package/version.js +1 -1
  236. package/version.mjs +1 -1
@@ -15,10 +15,15 @@ import type {
15
15
  ParsedChatCompletion,
16
16
  } from '../resources/chat/completions';
17
17
  import type { CompletionUsage } from '../resources/completions';
18
- import type { ChatCompletionRunner, ChatCompletionToolRunnerParams } from './ChatCompletionRunner';
18
+ import type {
19
+ ChatCompletionRunner,
20
+ ChatCompletionToolRunnerParamsWithContext,
21
+ ChatCompletionToolRunnerParamsWithoutContext,
22
+ } from './ChatCompletionRunner';
19
23
  import type {
20
24
  ChatCompletionStreamingRunner,
21
- ChatCompletionStreamingToolRunnerParams,
25
+ ChatCompletionStreamingToolRunnerParamsWithContext,
26
+ ChatCompletionStreamingToolRunnerParamsWithoutContext,
22
27
  } from './ChatCompletionStreamingRunner';
23
28
  import { isAssistantMessage, isToolMessage } from './chatCompletionUtils';
24
29
  import { BaseEvents, EventStream } from './EventStream';
@@ -285,16 +290,19 @@ export class AbstractChatCompletionRunner<
285
290
  return await this._createChatCompletion(client, params, options);
286
291
  }
287
292
 
288
- protected async _runTools<FunctionsArgs extends BaseFunctionsArgs>(
293
+ protected async _runTools<FunctionsArgs extends BaseFunctionsArgs, ToolContext>(
289
294
  client: OpenAI,
290
295
  params:
291
- | ChatCompletionToolRunnerParams<FunctionsArgs>
292
- | ChatCompletionStreamingToolRunnerParams<FunctionsArgs>,
296
+ | ChatCompletionToolRunnerParamsWithContext<FunctionsArgs, ToolContext>
297
+ | ChatCompletionToolRunnerParamsWithoutContext<FunctionsArgs>
298
+ | ChatCompletionStreamingToolRunnerParamsWithContext<FunctionsArgs, ToolContext>
299
+ | ChatCompletionStreamingToolRunnerParamsWithoutContext<FunctionsArgs>,
293
300
  runner: ChatCompletionRunner<any> | ChatCompletionStreamingRunner<any>,
294
301
  options?: RunnerOptions,
295
302
  ) {
296
303
  const role = 'tool' as const;
297
- const { tool_choice = 'auto', stream, ...restParams } = params;
304
+ const { tool_choice = 'auto', stream, toolContext: inputToolContext, ...restParams } = params;
305
+ const toolContext = inputToolContext as ToolContext;
298
306
  const singleFunctionToCall =
299
307
  typeof tool_choice !== 'string' && tool_choice.type === 'function' && tool_choice?.function?.name;
300
308
  const { maxChatCompletions = DEFAULT_MAX_CHAT_COMPLETIONS, afterCompletion } = options || {};
@@ -322,7 +330,7 @@ export class AbstractChatCompletionRunner<
322
330
  return tool as any as RunnableToolFunction<any>;
323
331
  });
324
332
 
325
- const functionsByName: Record<string, RunnableFunction<any>> = {};
333
+ const functionsByName: Record<string, RunnableFunction<any, ToolContext>> = {};
326
334
  for (const f of inputTools) {
327
335
  if (f.type === 'function') {
328
336
  functionsByName[f.function.name || f.function.function.name] = f.function;
@@ -389,9 +397,9 @@ export class AbstractChatCompletionRunner<
389
397
  const content = error instanceof Error ? error.message : String(error);
390
398
  return { message: { role, tool_call_id, content }, functionCalled: false };
391
399
  }
392
- rawContent = await fn.function(parsed, runner);
400
+ rawContent = await fn.function(parsed, runner, toolContext);
393
401
  } else {
394
- rawContent = await fn.function(args, runner);
402
+ rawContent = await fn.function(args, runner, toolContext);
395
403
  }
396
404
 
397
405
  const content = this.#stringifyFunctionCallResult(rawContent);
@@ -103,11 +103,12 @@ export class AssistantStream
103
103
 
104
104
  //Catch all for passing along all events
105
105
  this.on('event', (event) => {
106
+ const eventCopy = structuredClone(event);
106
107
  const reader = readQueue.shift();
107
108
  if (reader) {
108
- reader.resolve(event);
109
+ reader.resolve(eventCopy);
109
110
  } else {
110
- pushQueue.push(event);
111
+ pushQueue.push(eventCopy);
111
112
  }
112
113
  });
113
114
 
@@ -16,20 +16,60 @@ export interface ChatCompletionRunnerEvents extends AbstractChatCompletionRunner
16
16
  content: (content: string) => void;
17
17
  }
18
18
 
19
- export type ChatCompletionToolRunnerParams<FunctionsArgs extends BaseFunctionsArgs> = Omit<
20
- ChatCompletionCreateParamsNonStreaming,
21
- 'tools'
22
- > & {
23
- tools: RunnableTools<FunctionsArgs> | AutoParseableTool<any, true>[];
19
+ type ChatCompletionToolRunnerParamsBase = Omit<ChatCompletionCreateParamsNonStreaming, 'tools'>;
20
+
21
+ /**
22
+ * Parameters for tools that do not require a context value.
23
+ */
24
+ export type ChatCompletionToolRunnerParamsWithoutContext<FunctionsArgs extends BaseFunctionsArgs> =
25
+ ChatCompletionToolRunnerParamsBase & {
26
+ tools: RunnableTools<FunctionsArgs> | AutoParseableTool<any, true>[];
27
+ toolContext?: never;
28
+ };
29
+
30
+ /**
31
+ * Parameters for tools that require a context value.
32
+ */
33
+ export type ChatCompletionToolRunnerParamsWithContext<
34
+ FunctionsArgs extends BaseFunctionsArgs,
35
+ ToolContext,
36
+ > = ChatCompletionToolRunnerParamsBase & {
37
+ tools: RunnableTools<FunctionsArgs, ToolContext> | AutoParseableTool<any, true>[];
38
+ /**
39
+ * Context to pass to each tool callback during this run.
40
+ */
41
+ toolContext: ToolContext;
24
42
  };
25
43
 
44
+ /**
45
+ * Parameters for running tools. Supplying a context type makes `toolContext`
46
+ * required; omitting it preserves the existing no-context form.
47
+ */
48
+ export type ChatCompletionToolRunnerParams<FunctionsArgs extends BaseFunctionsArgs, ToolContext = never> = [
49
+ ToolContext,
50
+ ] extends [never] ?
51
+ ChatCompletionToolRunnerParamsWithoutContext<FunctionsArgs>
52
+ : ChatCompletionToolRunnerParamsWithContext<FunctionsArgs, ToolContext>;
53
+
26
54
  export class ChatCompletionRunner<ParsedT = null> extends AbstractChatCompletionRunner<
27
55
  ChatCompletionRunnerEvents,
28
56
  ParsedT
29
57
  > {
58
+ static runTools<ParsedT, ToolContext = unknown>(
59
+ client: OpenAI,
60
+ params: ChatCompletionToolRunnerParamsWithContext<any[], ToolContext>,
61
+ options?: RunnerOptions,
62
+ ): ChatCompletionRunner<ParsedT>;
30
63
  static runTools<ParsedT>(
31
64
  client: OpenAI,
32
- params: ChatCompletionToolRunnerParams<any[]>,
65
+ params: ChatCompletionToolRunnerParamsWithoutContext<any[]>,
66
+ options?: RunnerOptions,
67
+ ): ChatCompletionRunner<ParsedT>;
68
+ static runTools<ParsedT, ToolContext = unknown>(
69
+ client: OpenAI,
70
+ params:
71
+ | ChatCompletionToolRunnerParamsWithContext<any[], ToolContext>
72
+ | ChatCompletionToolRunnerParamsWithoutContext<any[]>,
33
73
  options?: RunnerOptions,
34
74
  ): ChatCompletionRunner<ParsedT> {
35
75
  const runner = new ChatCompletionRunner<ParsedT>();
@@ -26,6 +26,7 @@ import {
26
26
  type ChatCompletionCreateParams,
27
27
  type ChatCompletionCreateParamsBase,
28
28
  type ChatCompletionCreateParamsStreaming,
29
+ type ChatCompletionMessageParam,
29
30
  type ChatCompletionRole,
30
31
  } from '../resources/chat/completions/completions';
31
32
  import { Stream } from '../streaming';
@@ -118,6 +119,69 @@ export type ChatCompletionStreamParams = Omit<ChatCompletionCreateParamsBase, 's
118
119
  stream?: true;
119
120
  };
120
121
 
122
+ type ChatCompletionReadableStreamMessage = {
123
+ type: 'message';
124
+ message: ChatCompletionMessageParam;
125
+ tool_call_ids?: string[];
126
+ };
127
+
128
+ // Keep message records readable as empty chunks by older SDKs. Their finalizer
129
+ // overwrites `object`, so the encoded payload does not leak into completions.
130
+ const CHAT_COMPLETION_READABLE_STREAM_MESSAGE_PREFIX = 'chat.completion.chunk.message:';
131
+
132
+ type ChatCompletionReadableStreamMessageChunk = Pick<ChatCompletionChunk, 'id' | 'created' | 'model'> & {
133
+ choices: [];
134
+ object: `${typeof CHAT_COMPLETION_READABLE_STREAM_MESSAGE_PREFIX}${string}`;
135
+ };
136
+
137
+ export type ChatCompletionReadableStreamItem =
138
+ | ChatCompletionChunk
139
+ | ChatCompletionReadableStreamMessage
140
+ | ChatCompletionReadableStreamMessageChunk;
141
+
142
+ export function makeChatCompletionReadableStreamMessageChunk(
143
+ chunk: ChatCompletionChunk,
144
+ message: ChatCompletionMessageParam,
145
+ toolCallIds?: string[],
146
+ ): ChatCompletionReadableStreamMessageChunk {
147
+ const payload: ChatCompletionReadableStreamMessage = {
148
+ type: 'message',
149
+ message,
150
+ ...(toolCallIds ? { tool_call_ids: toolCallIds } : {}),
151
+ };
152
+
153
+ return {
154
+ id: chunk.id,
155
+ choices: [],
156
+ created: chunk.created,
157
+ model: chunk.model,
158
+ object: `${CHAT_COMPLETION_READABLE_STREAM_MESSAGE_PREFIX}${JSON.stringify(payload)}`,
159
+ };
160
+ }
161
+
162
+ function isChatCompletionReadableStreamMessage(
163
+ item: ChatCompletionReadableStreamItem,
164
+ ): item is ChatCompletionReadableStreamMessage | ChatCompletionReadableStreamMessageChunk {
165
+ return (
166
+ ('type' in item && item.type === 'message' && 'message' in item) ||
167
+ ('object' in item &&
168
+ typeof item.object === 'string' &&
169
+ item.object.startsWith(CHAT_COMPLETION_READABLE_STREAM_MESSAGE_PREFIX))
170
+ );
171
+ }
172
+
173
+ function getChatCompletionReadableStreamMessage(
174
+ item: ChatCompletionReadableStreamMessage | ChatCompletionReadableStreamMessageChunk,
175
+ ): ChatCompletionReadableStreamMessage {
176
+ if ('type' in item) {
177
+ return item;
178
+ }
179
+
180
+ return JSON.parse(
181
+ item.object.slice(CHAT_COMPLETION_READABLE_STREAM_MESSAGE_PREFIX.length),
182
+ ) as ChatCompletionReadableStreamMessage;
183
+ }
184
+
121
185
  interface ChoiceEventState {
122
186
  content_done: boolean;
123
187
  refusal_done: boolean;
@@ -205,27 +269,28 @@ export class ChatCompletionStream<ParsedT = null>
205
269
 
206
270
  for (const choice of chunk.choices) {
207
271
  const choiceSnapshot = completion.choices[choice.index]!;
272
+ const { delta } = choice;
208
273
 
209
274
  if (
210
- choice.delta.content != null &&
275
+ delta?.content != null &&
211
276
  choiceSnapshot.message?.role === 'assistant' &&
212
277
  choiceSnapshot.message?.content
213
278
  ) {
214
- this._emit('content', choice.delta.content, choiceSnapshot.message.content);
279
+ this._emit('content', delta.content, choiceSnapshot.message.content);
215
280
  this._emit('content.delta', {
216
- delta: choice.delta.content,
281
+ delta: delta.content,
217
282
  snapshot: choiceSnapshot.message.content,
218
283
  parsed: choiceSnapshot.message.parsed,
219
284
  });
220
285
  }
221
286
 
222
287
  if (
223
- choice.delta.refusal != null &&
288
+ delta?.refusal != null &&
224
289
  choiceSnapshot.message?.role === 'assistant' &&
225
290
  choiceSnapshot.message?.refusal
226
291
  ) {
227
292
  this._emit('refusal.delta', {
228
- delta: choice.delta.refusal,
293
+ delta: delta.refusal,
229
294
  snapshot: choiceSnapshot.message.refusal,
230
295
  });
231
296
  }
@@ -254,7 +319,7 @@ export class ChatCompletionStream<ParsedT = null>
254
319
  }
255
320
  }
256
321
 
257
- for (const toolCall of choice.delta.tool_calls ?? []) {
322
+ for (const toolCall of delta?.tool_calls ?? []) {
258
323
  if (state.current_tool_call_index !== toolCall.index) {
259
324
  this.#emitContentDoneEvents(choiceSnapshot);
260
325
 
@@ -267,7 +332,7 @@ export class ChatCompletionStream<ParsedT = null>
267
332
  state.current_tool_call_index = toolCall.index;
268
333
  }
269
334
 
270
- for (const toolCallDelta of choice.delta.tool_calls ?? []) {
335
+ for (const toolCallDelta of delta?.tool_calls ?? []) {
271
336
  const toolCallSnapshot = choiceSnapshot.message.tool_calls?.[toolCallDelta.index];
272
337
  if (!toolCallSnapshot?.type) {
273
338
  continue;
@@ -398,21 +463,51 @@ export class ChatCompletionStream<ParsedT = null>
398
463
  this._listenForAbort(options?.signal);
399
464
  this.#beginRequest();
400
465
  this._connected();
401
- const stream = Stream.fromReadableStream<ChatCompletionChunk>(readableStream, this.controller);
466
+ const stream = Stream.fromReadableStream<ChatCompletionReadableStreamItem>(
467
+ readableStream,
468
+ this.controller,
469
+ );
402
470
  let chatId;
403
- for await (const chunk of stream) {
404
- if (chatId && chatId !== chunk.id) {
471
+ for await (const item of stream) {
472
+ if (isChatCompletionReadableStreamMessage(item)) {
473
+ const message = getChatCompletionReadableStreamMessage(item);
474
+ if (this.#currentChatCompletionSnapshot) {
475
+ const toolCalls = this.#currentChatCompletionSnapshot.choices[0]?.message.tool_calls;
476
+ for (const [index, id] of message.tool_call_ids?.entries() ?? []) {
477
+ const toolCall = toolCalls?.[index];
478
+ if (toolCall && id) {
479
+ toolCall.id = id;
480
+ }
481
+ }
482
+
483
+ this._addChatCompletion(this.#endRequest());
484
+ chatId = undefined;
485
+ }
486
+ this._addMessage(message.message);
487
+ continue;
488
+ }
489
+
490
+ const chunk = item;
491
+
492
+ if (chatId && chunk.id && chatId !== chunk.id) {
405
493
  // A new request has been made.
406
494
  this._addChatCompletion(this.#endRequest());
407
495
  }
408
496
 
409
497
  this.#addChunk(chunk);
410
- chatId = chunk.id;
498
+ if (chunk.id) chatId = chunk.id;
411
499
  }
412
500
  if (stream.controller.signal?.aborted) {
413
501
  throw new APIUserAbortError();
414
502
  }
415
- return this._addChatCompletion(this.#endRequest());
503
+ if (this.#currentChatCompletionSnapshot) {
504
+ return this._addChatCompletion(this.#endRequest());
505
+ }
506
+ const lastChatCompletion = this._chatCompletions[this._chatCompletions.length - 1];
507
+ if (lastChatCompletion) {
508
+ return lastChatCompletion;
509
+ }
510
+ throw new OpenAIError(`request ended without sending any chunks`);
416
511
  }
417
512
 
418
513
  #getAutoParseableResponseFormat(): AutoParseableResponseFormat<ParsedT> | null {
@@ -432,7 +527,7 @@ export class ChatCompletionStream<ParsedT = null>
432
527
  ...rest,
433
528
  choices: [],
434
529
  };
435
- } else {
530
+ } else if (chunk.id) {
436
531
  Object.assign(snapshot, rest);
437
532
  }
438
533
 
@@ -1,26 +1,63 @@
1
1
  import {
2
2
  type ChatCompletionChunk,
3
3
  type ChatCompletionCreateParamsStreaming,
4
+ type ChatCompletionMessageParam,
4
5
  } from '../resources/chat/completions';
5
6
  import { RunnerOptions, type AbstractChatCompletionRunnerEvents } from './AbstractChatCompletionRunner';
6
7
  import { type ReadableStream } from '../internal/shim-types';
7
8
  import { RunnableTools, type BaseFunctionsArgs } from './RunnableFunction';
8
- import { ChatCompletionSnapshot, ChatCompletionStream } from './ChatCompletionStream';
9
+ import {
10
+ type ChatCompletionReadableStreamItem,
11
+ ChatCompletionSnapshot,
12
+ ChatCompletionStream,
13
+ makeChatCompletionReadableStreamMessageChunk,
14
+ } from './ChatCompletionStream';
15
+ import { OpenAIError } from '../error';
9
16
  import OpenAI from '../index';
10
17
  import { AutoParseableTool } from '../lib/parser';
18
+ import { Stream } from '../streaming';
19
+ import { isAssistantMessage, isToolMessage } from './chatCompletionUtils';
11
20
 
12
21
  export interface ChatCompletionStreamEvents extends AbstractChatCompletionRunnerEvents {
13
22
  content: (contentDelta: string, contentSnapshot: string) => void;
14
23
  chunk: (chunk: ChatCompletionChunk, snapshot: ChatCompletionSnapshot) => void;
15
24
  }
16
25
 
17
- export type ChatCompletionStreamingToolRunnerParams<FunctionsArgs extends BaseFunctionsArgs> = Omit<
18
- ChatCompletionCreateParamsStreaming,
19
- 'tools'
20
- > & {
21
- tools: RunnableTools<FunctionsArgs> | AutoParseableTool<any, true>[];
26
+ type ChatCompletionStreamingToolRunnerParamsBase = Omit<ChatCompletionCreateParamsStreaming, 'tools'>;
27
+
28
+ /**
29
+ * Parameters for tools that do not require a context value.
30
+ */
31
+ export type ChatCompletionStreamingToolRunnerParamsWithoutContext<FunctionsArgs extends BaseFunctionsArgs> =
32
+ ChatCompletionStreamingToolRunnerParamsBase & {
33
+ tools: RunnableTools<FunctionsArgs> | AutoParseableTool<any, true>[];
34
+ toolContext?: never;
35
+ };
36
+
37
+ /**
38
+ * Parameters for tools that require a context value.
39
+ */
40
+ export type ChatCompletionStreamingToolRunnerParamsWithContext<
41
+ FunctionsArgs extends BaseFunctionsArgs,
42
+ ToolContext,
43
+ > = ChatCompletionStreamingToolRunnerParamsBase & {
44
+ tools: RunnableTools<FunctionsArgs, ToolContext> | AutoParseableTool<any, true>[];
45
+ /**
46
+ * Context to pass to each tool callback during this run.
47
+ */
48
+ toolContext: ToolContext;
22
49
  };
23
50
 
51
+ /**
52
+ * Parameters for running streaming tools. Supplying a context type makes
53
+ * `toolContext` required; omitting it preserves the existing no-context form.
54
+ */
55
+ export type ChatCompletionStreamingToolRunnerParams<
56
+ FunctionsArgs extends BaseFunctionsArgs,
57
+ ToolContext = never,
58
+ > = [ToolContext] extends [never] ? ChatCompletionStreamingToolRunnerParamsWithoutContext<FunctionsArgs>
59
+ : ChatCompletionStreamingToolRunnerParamsWithContext<FunctionsArgs, ToolContext>;
60
+
24
61
  export class ChatCompletionStreamingRunner<ParsedT = null>
25
62
  extends ChatCompletionStream<ParsedT>
26
63
  implements AsyncIterable<ChatCompletionChunk>
@@ -31,9 +68,109 @@ export class ChatCompletionStreamingRunner<ParsedT = null>
31
68
  return runner;
32
69
  }
33
70
 
71
+ override toReadableStream(): ReadableStream {
72
+ const pushQueue: ChatCompletionReadableStreamItem[] = [];
73
+ const readQueue: {
74
+ resolve: (event: ChatCompletionReadableStreamItem | undefined) => void;
75
+ reject: (err: unknown) => void;
76
+ }[] = [];
77
+ let done = false;
78
+ let lastChunk: ChatCompletionChunk | undefined;
79
+ let toolCallIds: string[] | undefined;
80
+
81
+ const pushEvent = (event: ChatCompletionReadableStreamItem) => {
82
+ const reader = readQueue.shift();
83
+ if (reader) {
84
+ reader.resolve(event);
85
+ } else {
86
+ pushQueue.push(event);
87
+ }
88
+ };
89
+
90
+ this.on('chunk', (chunk) => {
91
+ lastChunk = chunk;
92
+ pushEvent(chunk);
93
+ });
94
+ this.on('message', (message: ChatCompletionMessageParam) => {
95
+ if (isAssistantMessage(message)) {
96
+ toolCallIds = message.tool_calls?.map((toolCall) => toolCall.id);
97
+ return;
98
+ }
99
+
100
+ if (isToolMessage(message)) {
101
+ if (!lastChunk) {
102
+ throw new OpenAIError('cannot serialize a tool message before receiving any chunks');
103
+ }
104
+ pushEvent(makeChatCompletionReadableStreamMessageChunk(lastChunk, message, toolCallIds));
105
+ }
106
+ });
107
+
108
+ this.on('end', () => {
109
+ done = true;
110
+ for (const reader of readQueue) {
111
+ reader.resolve(undefined);
112
+ }
113
+ readQueue.length = 0;
114
+ });
115
+
116
+ this.on('abort', (err) => {
117
+ done = true;
118
+ for (const reader of readQueue) {
119
+ reader.reject(err);
120
+ }
121
+ readQueue.length = 0;
122
+ });
123
+
124
+ this.on('error', (err) => {
125
+ done = true;
126
+ for (const reader of readQueue) {
127
+ reader.reject(err);
128
+ }
129
+ readQueue.length = 0;
130
+ });
131
+
132
+ const iterator = (): AsyncIterator<ChatCompletionReadableStreamItem> => ({
133
+ next: async (): Promise<IteratorResult<ChatCompletionReadableStreamItem>> => {
134
+ if (!pushQueue.length) {
135
+ if (done) {
136
+ return { value: undefined, done: true };
137
+ }
138
+ return new Promise<ChatCompletionReadableStreamItem | undefined>((resolve, reject) =>
139
+ readQueue.push({ resolve, reject }),
140
+ ).then((event) => (event ? { value: event, done: false } : { value: undefined, done: true }));
141
+ }
142
+ const event = pushQueue.shift();
143
+ if (!event) {
144
+ return { value: undefined, done: true };
145
+ }
146
+ return { value: event, done: false };
147
+ },
148
+ return: async () => {
149
+ this.abort();
150
+ return { value: undefined, done: true };
151
+ },
152
+ });
153
+
154
+ const stream = new Stream(iterator, this.controller);
155
+ return stream.toReadableStream();
156
+ }
157
+
158
+ static runTools<T extends (string | object)[], ParsedT = null, ToolContext = unknown>(
159
+ client: OpenAI,
160
+ params: ChatCompletionStreamingToolRunnerParamsWithContext<T, ToolContext>,
161
+ options?: RunnerOptions,
162
+ ): ChatCompletionStreamingRunner<ParsedT>;
163
+
34
164
  static runTools<T extends (string | object)[], ParsedT = null>(
35
165
  client: OpenAI,
36
- params: ChatCompletionStreamingToolRunnerParams<T>,
166
+ params: ChatCompletionStreamingToolRunnerParamsWithoutContext<T>,
167
+ options?: RunnerOptions,
168
+ ): ChatCompletionStreamingRunner<ParsedT>;
169
+ static runTools<T extends (string | object)[], ParsedT = null, ToolContext = unknown>(
170
+ client: OpenAI,
171
+ params:
172
+ | ChatCompletionStreamingToolRunnerParamsWithContext<T, ToolContext>
173
+ | ChatCompletionStreamingToolRunnerParamsWithoutContext<T>,
37
174
  options?: RunnerOptions,
38
175
  ): ChatCompletionStreamingRunner<ParsedT> {
39
176
  const runner = new ChatCompletionStreamingRunner<ParsedT>(
@@ -44,10 +44,17 @@ export class EventStream<EventTypes extends BaseEvents> {
44
44
  // Unfortunately if we call `executor()` immediately we get runtime errors about
45
45
  // references to `this` before the `super()` constructor call returns.
46
46
  setTimeout(() => {
47
- executor().then(() => {
48
- this._emitFinal();
49
- this._emit('end');
50
- }, this.#handleError.bind(this));
47
+ Promise.resolve()
48
+ .then(executor)
49
+ .then(() => {
50
+ try {
51
+ this._emitFinal();
52
+ } catch (error) {
53
+ this.#handleError(error);
54
+ return;
55
+ }
56
+ this._emit('end');
57
+ }, this.#handleError.bind(this));
51
58
  }, 0);
52
59
  }
53
60
 
@@ -157,6 +164,109 @@ export class EventStream<EventTypes extends BaseEvents> {
157
164
  });
158
165
  }
159
166
 
167
+ /**
168
+ * Returns an async iterator that yields every time the event is triggered.
169
+ * The iterator ends when the stream ends and rejects if the stream errors
170
+ * or is aborted. If you request the 'error' or 'abort' event, the iterator
171
+ * yields that event instead of rejecting.
172
+ *
173
+ * Example:
174
+ *
175
+ * for await (const [message] of stream.events('message')) {
176
+ * await processMessage(message);
177
+ * }
178
+ */
179
+ events<Event extends keyof EventTypes>(
180
+ event: Event,
181
+ ): AsyncIterableIterator<EventParameters<EventTypes, Event>> {
182
+ type Parameters = EventParameters<EventTypes, Event>;
183
+ type Result = IteratorResult<Parameters>;
184
+ type Reader = {
185
+ resolve: (result: Result) => void;
186
+ reject: (error: OpenAIError) => void;
187
+ };
188
+
189
+ const pushQueue: Parameters[] = [];
190
+ const readQueue: Reader[] = [];
191
+ let ended = this.ended;
192
+ let failure: OpenAIError | undefined;
193
+ let failureDelivered = false;
194
+
195
+ const doneResult = (): Result => ({ value: undefined as never, done: true });
196
+ const finishReaders = () => {
197
+ while (readQueue.length) {
198
+ readQueue.shift()!.resolve(doneResult());
199
+ }
200
+ };
201
+ const rejectReader = () => {
202
+ if (!failure || failureDelivered || !readQueue.length) return;
203
+ failureDelivered = true;
204
+ readQueue.shift()!.reject(failure);
205
+ };
206
+ const cleanup = () => {
207
+ this.off(event, onEvent as EventListener<EventTypes, Event>);
208
+ this.off('end', onEnd);
209
+ if (event !== 'error') this.off('error', onFailure);
210
+ if (event !== 'abort') this.off('abort', onFailure);
211
+ };
212
+ const onEvent = (...args: Parameters) => {
213
+ if (ended) return;
214
+ const reader = readQueue.shift();
215
+ if (reader) {
216
+ reader.resolve({ value: args, done: false });
217
+ } else {
218
+ pushQueue.push(args);
219
+ }
220
+ };
221
+ const onFailure = (error: OpenAIError) => {
222
+ failure = error;
223
+ if (!pushQueue.length) rejectReader();
224
+ };
225
+ const onEnd = () => {
226
+ ended = true;
227
+ cleanup();
228
+ if (!pushQueue.length) {
229
+ rejectReader();
230
+ finishReaders();
231
+ }
232
+ };
233
+
234
+ if (!ended) {
235
+ this.on(event, onEvent as EventListener<EventTypes, Event>);
236
+ this.on('end', onEnd);
237
+ if (event !== 'error') this.on('error', onFailure);
238
+ if (event !== 'abort') this.on('abort', onFailure);
239
+ }
240
+
241
+ return {
242
+ next: () => {
243
+ const value = pushQueue.shift();
244
+ if (value) return Promise.resolve({ value, done: false });
245
+
246
+ if (failure && !failureDelivered) {
247
+ failureDelivered = true;
248
+ return Promise.reject(failure);
249
+ }
250
+
251
+ if (ended) return Promise.resolve(doneResult());
252
+
253
+ return new Promise<Result>((resolve, reject) => {
254
+ readQueue.push({ resolve, reject });
255
+ });
256
+ },
257
+ return: () => {
258
+ ended = true;
259
+ pushQueue.length = 0;
260
+ cleanup();
261
+ finishReaders();
262
+ return Promise.resolve(doneResult());
263
+ },
264
+ [Symbol.asyncIterator]() {
265
+ return this;
266
+ },
267
+ };
268
+ }
269
+
160
270
  async done(): Promise<void> {
161
271
  this.#catchingPromiseCreated = true;
162
272
  await this.#endPromise;