openai 7.28.0 → 7.30.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 (291) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/client.d.mts +3 -0
  3. package/client.d.mts.map +1 -1
  4. package/client.d.ts +3 -0
  5. package/client.d.ts.map +1 -1
  6. package/client.js +3 -0
  7. package/client.js.map +1 -1
  8. package/client.mjs +3 -0
  9. package/client.mjs.map +1 -1
  10. package/helpers/standard-schema.d.mts +8 -3
  11. package/helpers/standard-schema.d.mts.map +1 -1
  12. package/helpers/standard-schema.d.ts +8 -3
  13. package/helpers/standard-schema.d.ts.map +1 -1
  14. package/helpers/standard-schema.js +1 -0
  15. package/helpers/standard-schema.js.map +1 -1
  16. package/helpers/standard-schema.mjs +1 -0
  17. package/helpers/standard-schema.mjs.map +1 -1
  18. package/helpers/zod.d.mts +2 -0
  19. package/helpers/zod.d.mts.map +1 -1
  20. package/helpers/zod.d.ts +2 -0
  21. package/helpers/zod.d.ts.map +1 -1
  22. package/helpers/zod.js +1 -0
  23. package/helpers/zod.js.map +1 -1
  24. package/helpers/zod.mjs +1 -0
  25. package/helpers/zod.mjs.map +1 -1
  26. package/lib/ChatCompletionStream.d.mts +2 -1
  27. package/lib/ChatCompletionStream.d.mts.map +1 -1
  28. package/lib/ChatCompletionStream.d.ts +2 -1
  29. package/lib/ChatCompletionStream.d.ts.map +1 -1
  30. package/lib/ChatCompletionStream.js +9 -2
  31. package/lib/ChatCompletionStream.js.map +1 -1
  32. package/lib/ChatCompletionStream.mjs +9 -2
  33. package/lib/ChatCompletionStream.mjs.map +1 -1
  34. package/lib/ResponsesParser.d.mts.map +1 -1
  35. package/lib/ResponsesParser.d.ts.map +1 -1
  36. package/lib/ResponsesParser.js +5 -0
  37. package/lib/ResponsesParser.js.map +1 -1
  38. package/lib/ResponsesParser.mjs +5 -0
  39. package/lib/ResponsesParser.mjs.map +1 -1
  40. package/lib/agents/agent-session-stream.d.mts +7 -0
  41. package/lib/agents/agent-session-stream.d.mts.map +1 -1
  42. package/lib/agents/agent-session-stream.d.ts +7 -0
  43. package/lib/agents/agent-session-stream.d.ts.map +1 -1
  44. package/lib/agents/agent-session-stream.js +8 -110
  45. package/lib/agents/agent-session-stream.js.map +1 -1
  46. package/lib/agents/agent-session-stream.mjs +9 -111
  47. package/lib/agents/agent-session-stream.mjs.map +1 -1
  48. package/lib/beta/agents/agent-session-create-stream.d.mts +41 -1
  49. package/lib/beta/agents/agent-session-create-stream.d.mts.map +1 -1
  50. package/lib/beta/agents/agent-session-create-stream.d.ts +41 -1
  51. package/lib/beta/agents/agent-session-create-stream.d.ts.map +1 -1
  52. package/lib/beta/agents/agent-session-create-stream.js +70 -2
  53. package/lib/beta/agents/agent-session-create-stream.js.map +1 -1
  54. package/lib/beta/agents/agent-session-create-stream.mjs +69 -2
  55. package/lib/beta/agents/agent-session-create-stream.mjs.map +1 -1
  56. package/lib/beta/agents/function-tool.d.mts.map +1 -1
  57. package/lib/beta/agents/function-tool.d.ts.map +1 -1
  58. package/lib/beta/agents/function-tool.js +22 -15
  59. package/lib/beta/agents/function-tool.js.map +1 -1
  60. package/lib/beta/agents/function-tool.mjs +22 -15
  61. package/lib/beta/agents/function-tool.mjs.map +1 -1
  62. package/lib/beta/agents/parse-result.d.mts +2 -0
  63. package/lib/beta/agents/parse-result.d.mts.map +1 -1
  64. package/lib/beta/agents/parse-result.d.ts +2 -0
  65. package/lib/beta/agents/parse-result.d.ts.map +1 -1
  66. package/lib/beta/agents/tool-dispatcher.d.mts +13 -0
  67. package/lib/beta/agents/tool-dispatcher.d.mts.map +1 -0
  68. package/lib/beta/agents/tool-dispatcher.d.ts +13 -0
  69. package/lib/beta/agents/tool-dispatcher.d.ts.map +1 -0
  70. package/lib/beta/agents/tool-dispatcher.js +185 -0
  71. package/lib/beta/agents/tool-dispatcher.js.map +1 -0
  72. package/lib/beta/agents/tool-dispatcher.mjs +181 -0
  73. package/lib/beta/agents/tool-dispatcher.mjs.map +1 -0
  74. package/lib/beta/agents/tool-error.d.mts +13 -0
  75. package/lib/beta/agents/tool-error.d.mts.map +1 -0
  76. package/lib/beta/agents/tool-error.d.ts +13 -0
  77. package/lib/beta/agents/tool-error.d.ts.map +1 -0
  78. package/lib/beta/agents/tool-error.js +3 -0
  79. package/lib/beta/agents/tool-error.js.map +1 -0
  80. package/lib/beta/agents/tool-error.mjs +2 -0
  81. package/lib/beta/agents/tool-error.mjs.map +1 -0
  82. package/lib/beta/agents/tool-stages.d.mts +12 -0
  83. package/lib/beta/agents/tool-stages.d.mts.map +1 -0
  84. package/lib/beta/agents/tool-stages.d.ts +12 -0
  85. package/lib/beta/agents/tool-stages.d.ts.map +1 -0
  86. package/lib/beta/agents/tool-stages.js +6 -0
  87. package/lib/beta/agents/tool-stages.js.map +1 -0
  88. package/lib/beta/agents/tool-stages.mjs +3 -0
  89. package/lib/beta/agents/tool-stages.mjs.map +1 -0
  90. package/lib/chat-completions/streaming.d.mts +11 -0
  91. package/lib/chat-completions/streaming.d.mts.map +1 -0
  92. package/lib/chat-completions/streaming.d.ts +11 -0
  93. package/lib/chat-completions/streaming.d.ts.map +1 -0
  94. package/lib/chat-completions/streaming.js +27 -0
  95. package/lib/chat-completions/streaming.js.map +1 -0
  96. package/lib/chat-completions/streaming.mjs +23 -0
  97. package/lib/chat-completions/streaming.mjs.map +1 -0
  98. package/package.json +1 -1
  99. package/resources/admin/organization/usage.d.mts +304 -9
  100. package/resources/admin/organization/usage.d.mts.map +1 -1
  101. package/resources/admin/organization/usage.d.ts +304 -9
  102. package/resources/admin/organization/usage.d.ts.map +1 -1
  103. package/resources/beta/agents/agents.d.mts +12 -6
  104. package/resources/beta/agents/agents.d.mts.map +1 -1
  105. package/resources/beta/agents/agents.d.ts +12 -6
  106. package/resources/beta/agents/agents.d.ts.map +1 -1
  107. package/resources/beta/agents/agents.js.map +1 -1
  108. package/resources/beta/agents/agents.mjs.map +1 -1
  109. package/resources/beta/agents/environments/templates.d.mts +6 -8
  110. package/resources/beta/agents/environments/templates.d.mts.map +1 -1
  111. package/resources/beta/agents/environments/templates.d.ts +6 -8
  112. package/resources/beta/agents/environments/templates.d.ts.map +1 -1
  113. package/resources/beta/agents/sessions/index.d.mts +1 -1
  114. package/resources/beta/agents/sessions/index.d.mts.map +1 -1
  115. package/resources/beta/agents/sessions/index.d.ts +1 -1
  116. package/resources/beta/agents/sessions/index.d.ts.map +1 -1
  117. package/resources/beta/agents/sessions/index.js +2 -2
  118. package/resources/beta/agents/sessions/index.js.map +1 -1
  119. package/resources/beta/agents/sessions/index.mjs +1 -1
  120. package/resources/beta/agents/sessions/index.mjs.map +1 -1
  121. package/resources/beta/agents/sessions/sessions.d.mts +6 -3
  122. package/resources/beta/agents/sessions/sessions.d.mts.map +1 -1
  123. package/resources/beta/agents/sessions/sessions.d.ts +6 -3
  124. package/resources/beta/agents/sessions/sessions.d.ts.map +1 -1
  125. package/resources/beta/agents/sessions/sessions.js +7 -4
  126. package/resources/beta/agents/sessions/sessions.js.map +1 -1
  127. package/resources/beta/agents/sessions/sessions.mjs +8 -5
  128. package/resources/beta/agents/sessions/sessions.mjs.map +1 -1
  129. package/resources/beta/agents/sessions/subagents/turns/turns.d.mts +2 -2
  130. package/resources/beta/agents/sessions/subagents/turns/turns.d.mts.map +1 -1
  131. package/resources/beta/agents/sessions/subagents/turns/turns.d.ts +2 -2
  132. package/resources/beta/agents/sessions/subagents/turns/turns.d.ts.map +1 -1
  133. package/resources/beta/agents/sessions/turns/index.d.mts +3 -0
  134. package/resources/beta/agents/sessions/turns/index.d.mts.map +1 -0
  135. package/resources/beta/agents/sessions/turns/index.d.ts +3 -0
  136. package/resources/beta/agents/sessions/turns/index.d.ts.map +1 -0
  137. package/resources/beta/agents/sessions/turns/index.js +9 -0
  138. package/resources/beta/agents/sessions/turns/index.js.map +1 -0
  139. package/resources/beta/agents/sessions/turns/index.mjs +4 -0
  140. package/resources/beta/agents/sessions/turns/index.mjs.map +1 -0
  141. package/resources/beta/agents/sessions/turns/items.d.mts +43 -0
  142. package/resources/beta/agents/sessions/turns/items.d.mts.map +1 -0
  143. package/resources/beta/agents/sessions/turns/items.d.ts +43 -0
  144. package/resources/beta/agents/sessions/turns/items.d.ts.map +1 -0
  145. package/resources/beta/agents/sessions/turns/items.js +40 -0
  146. package/resources/beta/agents/sessions/turns/items.js.map +1 -0
  147. package/resources/beta/agents/sessions/turns/items.mjs +36 -0
  148. package/resources/beta/agents/sessions/turns/items.mjs.map +1 -0
  149. package/resources/beta/agents/sessions/turns/turns.d.mts +128 -0
  150. package/resources/beta/agents/sessions/turns/turns.d.mts.map +1 -0
  151. package/resources/beta/agents/sessions/turns/turns.d.ts +128 -0
  152. package/resources/beta/agents/sessions/turns/turns.d.ts.map +1 -0
  153. package/resources/beta/agents/sessions/turns/turns.js +111 -0
  154. package/resources/beta/agents/sessions/turns/turns.js.map +1 -0
  155. package/resources/beta/agents/sessions/turns/turns.mjs +106 -0
  156. package/resources/beta/agents/sessions/turns/turns.mjs.map +1 -0
  157. package/resources/beta/agents/sessions/turns.d.mts +1 -123
  158. package/resources/beta/agents/sessions/turns.d.mts.map +1 -1
  159. package/resources/beta/agents/sessions/turns.d.ts +1 -123
  160. package/resources/beta/agents/sessions/turns.d.ts.map +1 -1
  161. package/resources/beta/agents/sessions/turns.js +2 -99
  162. package/resources/beta/agents/sessions/turns.js.map +1 -1
  163. package/resources/beta/agents/sessions/turns.mjs +1 -97
  164. package/resources/beta/agents/sessions/turns.mjs.map +1 -1
  165. package/resources/beta/beta.d.mts +2 -2
  166. package/resources/beta/beta.d.mts.map +1 -1
  167. package/resources/beta/beta.d.ts +2 -2
  168. package/resources/beta/beta.d.ts.map +1 -1
  169. package/resources/beta/beta.js.map +1 -1
  170. package/resources/beta/beta.mjs.map +1 -1
  171. package/resources/beta/index.d.mts +1 -1
  172. package/resources/beta/index.d.mts.map +1 -1
  173. package/resources/beta/index.d.ts +1 -1
  174. package/resources/beta/index.d.ts.map +1 -1
  175. package/resources/beta/index.js.map +1 -1
  176. package/resources/beta/index.mjs.map +1 -1
  177. package/resources/beta/responses/index.d.mts +1 -1
  178. package/resources/beta/responses/index.d.mts.map +1 -1
  179. package/resources/beta/responses/index.d.ts +1 -1
  180. package/resources/beta/responses/index.d.ts.map +1 -1
  181. package/resources/beta/responses/index.js.map +1 -1
  182. package/resources/beta/responses/index.mjs.map +1 -1
  183. package/resources/beta/responses/responses.d.mts +378 -6
  184. package/resources/beta/responses/responses.d.mts.map +1 -1
  185. package/resources/beta/responses/responses.d.ts +378 -6
  186. package/resources/beta/responses/responses.d.ts.map +1 -1
  187. package/resources/beta/responses/responses.js.map +1 -1
  188. package/resources/beta/responses/responses.mjs.map +1 -1
  189. package/resources/chat/completions/completions.d.mts +42 -8
  190. package/resources/chat/completions/completions.d.mts.map +1 -1
  191. package/resources/chat/completions/completions.d.ts +42 -8
  192. package/resources/chat/completions/completions.d.ts.map +1 -1
  193. package/resources/chat/completions/completions.js +5 -2
  194. package/resources/chat/completions/completions.js.map +1 -1
  195. package/resources/chat/completions/completions.mjs +5 -2
  196. package/resources/chat/completions/completions.mjs.map +1 -1
  197. package/resources/completions.d.mts +1 -1
  198. package/resources/completions.d.mts.map +1 -1
  199. package/resources/completions.d.ts +1 -1
  200. package/resources/completions.d.ts.map +1 -1
  201. package/resources/decisions.d.mts +196 -0
  202. package/resources/decisions.d.mts.map +1 -0
  203. package/resources/decisions.d.ts +196 -0
  204. package/resources/decisions.d.ts.map +1 -0
  205. package/resources/decisions.js +32 -0
  206. package/resources/decisions.js.map +1 -0
  207. package/resources/decisions.mjs +28 -0
  208. package/resources/decisions.mjs.map +1 -0
  209. package/resources/fine-tuning/jobs/jobs.d.mts +7 -2
  210. package/resources/fine-tuning/jobs/jobs.d.mts.map +1 -1
  211. package/resources/fine-tuning/jobs/jobs.d.ts +7 -2
  212. package/resources/fine-tuning/jobs/jobs.d.ts.map +1 -1
  213. package/resources/fine-tuning/jobs/jobs.js.map +1 -1
  214. package/resources/fine-tuning/jobs/jobs.mjs.map +1 -1
  215. package/resources/graders/grader-models.d.mts +1 -1
  216. package/resources/graders/grader-models.d.ts +1 -1
  217. package/resources/images.d.mts +4 -3
  218. package/resources/images.d.mts.map +1 -1
  219. package/resources/images.d.ts +4 -3
  220. package/resources/images.d.ts.map +1 -1
  221. package/resources/index.d.mts +1 -0
  222. package/resources/index.d.mts.map +1 -1
  223. package/resources/index.d.ts +1 -0
  224. package/resources/index.d.ts.map +1 -1
  225. package/resources/index.js +3 -1
  226. package/resources/index.js.map +1 -1
  227. package/resources/index.mjs +1 -0
  228. package/resources/index.mjs.map +1 -1
  229. package/resources/realtime/realtime.d.mts +10 -0
  230. package/resources/realtime/realtime.d.mts.map +1 -1
  231. package/resources/realtime/realtime.d.ts +10 -0
  232. package/resources/realtime/realtime.d.ts.map +1 -1
  233. package/resources/realtime/realtime.js.map +1 -1
  234. package/resources/realtime/realtime.mjs.map +1 -1
  235. package/resources/responses/responses.d.mts +380 -6
  236. package/resources/responses/responses.d.mts.map +1 -1
  237. package/resources/responses/responses.d.ts +380 -6
  238. package/resources/responses/responses.d.ts.map +1 -1
  239. package/resources/responses/responses.js.map +1 -1
  240. package/resources/responses/responses.mjs.map +1 -1
  241. package/resources/shared.d.mts +5 -0
  242. package/resources/shared.d.mts.map +1 -1
  243. package/resources/shared.d.ts +5 -0
  244. package/resources/shared.d.ts.map +1 -1
  245. package/resources/videos.d.mts +5 -0
  246. package/resources/videos.d.mts.map +1 -1
  247. package/resources/videos.d.ts +5 -0
  248. package/resources/videos.d.ts.map +1 -1
  249. package/src/client.ts +21 -0
  250. package/src/helpers/standard-schema.ts +11 -4
  251. package/src/helpers/zod.ts +4 -0
  252. package/src/lib/ChatCompletionStream.ts +10 -3
  253. package/src/lib/ResponsesParser.ts +8 -0
  254. package/src/lib/agents/agent-session-stream.ts +20 -124
  255. package/src/lib/beta/agents/agent-session-create-stream.ts +87 -1
  256. package/src/lib/beta/agents/function-tool.ts +23 -15
  257. package/src/lib/beta/agents/tool-dispatcher.ts +226 -0
  258. package/src/lib/beta/agents/tool-error.ts +12 -0
  259. package/src/lib/beta/agents/tool-stages.ts +14 -0
  260. package/src/lib/chat-completions/streaming.ts +35 -0
  261. package/src/resources/admin/organization/usage.ts +350 -9
  262. package/src/resources/beta/agents/agents.ts +14 -5
  263. package/src/resources/beta/agents/environments/templates.ts +6 -8
  264. package/src/resources/beta/agents/sessions/index.ts +7 -1
  265. package/src/resources/beta/agents/sessions/sessions.ts +16 -4
  266. package/src/resources/beta/agents/sessions/subagents/turns/turns.ts +2 -2
  267. package/src/resources/beta/agents/sessions/turns/index.ts +4 -0
  268. package/src/resources/beta/agents/sessions/turns/items.ts +79 -0
  269. package/src/resources/beta/agents/sessions/turns/turns.ts +383 -0
  270. package/src/resources/beta/agents/sessions/turns.ts +1 -373
  271. package/src/resources/beta/beta.ts +2 -0
  272. package/src/resources/beta/index.ts +1 -0
  273. package/src/resources/beta/responses/index.ts +1 -0
  274. package/src/resources/beta/responses/responses.ts +470 -5
  275. package/src/resources/chat/completions/completions.ts +65 -18
  276. package/src/resources/completions.ts +1 -1
  277. package/src/resources/decisions.ts +290 -0
  278. package/src/resources/fine-tuning/jobs/jobs.ts +7 -2
  279. package/src/resources/graders/grader-models.ts +1 -1
  280. package/src/resources/images.ts +4 -3
  281. package/src/resources/index.ts +9 -0
  282. package/src/resources/realtime/realtime.ts +10 -0
  283. package/src/resources/responses/api.md +1 -0
  284. package/src/resources/responses/responses.ts +472 -5
  285. package/src/resources/shared.ts +6 -0
  286. package/src/resources/videos.ts +6 -0
  287. package/src/version.ts +1 -1
  288. package/version.d.mts +1 -1
  289. package/version.d.ts +1 -1
  290. package/version.js +1 -1
  291. package/version.mjs +1 -1
@@ -1,3 +1,10 @@
1
+ import { APIUserAbortError, OpenAIError } from '../../../core/error';
2
+ import { buildHeaders } from '../../../internal/headers';
3
+ import type { RequestOptions } from '../../../internal/request-options';
4
+ import type { Sessions, SessionCreateParams } from '../../../resources/beta/agents/sessions/sessions';
5
+ import type { AgentToolHandler } from '../../agents/agent-session-stream';
6
+ import { TurnState } from '../../agents/turn-state';
7
+ import { AgentToolDispatcher } from './tool-dispatcher';
1
8
  import type { AgentOutputFormat, AgentResult } from './output-format-types';
2
9
  import type { Stream } from '../../../core/streaming';
3
10
  import type { AgentSessionEvent } from '../../../resources/beta/agents/agents';
@@ -12,16 +19,57 @@ export type AgentSessionCreateStream<T = never> = Stream<AgentSessionEvent> & {
12
19
  withResultCollection: () => AgentSessionCreateStream<T>;
13
20
  };
14
21
 
22
+ async function* dispatchCreationTools(
23
+ source: () => AsyncIterator<AgentSessionEvent>,
24
+ dispatcher: AgentToolDispatcher,
25
+ signal: AbortSignal,
26
+ ): AsyncGenerator<AgentSessionEvent> {
27
+ const state = new TurnState();
28
+ let sessionID: string | undefined;
29
+ for await (const event of { [Symbol.asyncIterator]: source }) {
30
+ if (event.type === 'agent.session.created') {
31
+ sessionID ??= event.session.id;
32
+ }
33
+ const dispatch = state.accept(event) ? dispatcher.prepare(state.call(event), sessionID) : undefined;
34
+ yield event;
35
+ if (signal.aborted) {
36
+ return;
37
+ }
38
+ try {
39
+ await dispatch?.();
40
+ } catch (error) {
41
+ if (signal.aborted && error instanceof APIUserAbortError) {
42
+ return;
43
+ }
44
+ throw error;
45
+ }
46
+ }
47
+ }
48
+
15
49
  /** Add beta result collection without replacing custom stream instances.
16
50
  * @internal
17
51
  */
18
52
  export function withAgentTurnResult<T = never>(
19
53
  stream: Stream<AgentSessionEvent>,
20
54
  format?: AgentOutputFormat<T>,
55
+ tools?: {
56
+ sessions: Sessions;
57
+ handlers: Record<string, AgentToolHandler>;
58
+ options?: RequestOptions | undefined;
59
+ },
21
60
  ): AgentSessionCreateStream<T> {
22
61
  let collection: ResultCollection;
23
62
  stream.__betaTransformIterator((source) => {
24
- collection = new ResultCollection(source, undefined, undefined, stream.controller.signal);
63
+ // A tool result has its own POST endpoint, independent of creation overrides.
64
+ const { path: _path, method: _method, stream: _stream, ...options } = tools?.options ?? {};
65
+ const dispatcher =
66
+ tools && new AgentToolDispatcher(tools.sessions, tools.handlers, stream.controller, options);
67
+ collection = new ResultCollection(
68
+ dispatcher ? () => dispatchCreationTools(source, dispatcher, stream.controller.signal) : source,
69
+ dispatcher ? (name) => dispatcher.canHandle(name) : undefined,
70
+ undefined,
71
+ stream.controller.signal,
72
+ );
25
73
  return () => collection.iterate();
26
74
  });
27
75
  let parsed: Promise<AgentResult<T>> | undefined;
@@ -34,3 +82,41 @@ export function withAgentTurnResult<T = never>(
34
82
  });
35
83
  return result;
36
84
  }
85
+
86
+ /** Remove local callbacks without changing ordinary creation requests. @internal */
87
+ export function captureCreationTools(body: SessionCreateParams, options?: RequestOptions) {
88
+ const descriptor = Object.getOwnPropertyDescriptor(body, 'toolHandlers');
89
+ if (!descriptor) {
90
+ return { body, options };
91
+ }
92
+ const handlers = body.toolHandlers;
93
+ if (handlers === undefined && 'value' in descriptor) {
94
+ return { body, options };
95
+ }
96
+ const descriptors = Object.getOwnPropertyDescriptors(body);
97
+ delete descriptors.toolHandlers;
98
+ const capturedOptions = { ...options };
99
+ if ('toJSON' in body || capturedOptions.body !== undefined) {
100
+ throw new OpenAIError('Creation tool handlers cannot customize request body serialization');
101
+ }
102
+ if (handlers === undefined) {
103
+ // SAFETY: Preserve request fields while removing the accessor so serialization cannot evaluate it again.
104
+ return {
105
+ body: Object.create(Object.getPrototypeOf(body), descriptors) as SessionCreateParams,
106
+ options: capturedOptions,
107
+ };
108
+ }
109
+ const { stream } = body;
110
+ if (stream !== true) {
111
+ throw new OpenAIError('toolHandlers requires stream: true');
112
+ }
113
+ capturedOptions.headers = buildHeaders([capturedOptions.headers]);
114
+ descriptors.stream = { value: stream, enumerable: true, configurable: true, writable: true };
115
+ // SAFETY: Preserve request fields, omitting callbacks and fixing the validated streaming mode.
116
+ const request = Object.create(Object.getPrototypeOf(body), descriptors) as SessionCreateParams;
117
+ return {
118
+ body: request,
119
+ options: capturedOptions,
120
+ handlers: handlers && Object.fromEntries(Object.entries(handlers)),
121
+ };
122
+ }
@@ -3,6 +3,8 @@ import type { AgentToolHandler } from '../../agents/agent-session-stream';
3
3
  import type { AutoParseableResponseTool } from '../../ResponsesParser';
4
4
  import type { AgentToolParam } from '../../../resources/beta/agents/agents';
5
5
  import { isInputContent } from './tool-output';
6
+ import { toolStages } from './tool-stages';
7
+ import type { StagedToolHandler } from './tool-stages';
6
8
 
7
9
  /** A beta Agents function definition paired with its local, validating handler. */
8
10
  export interface AgentFunctionTool {
@@ -32,6 +34,26 @@ export function functionTool<Arguments>(
32
34
  if (!execute || !tool.parameters) {
33
35
  throw new OpenAIError('Agents function tools require a callback and argument schema');
34
36
  }
37
+ const handler: StagedToolHandler = async (arguments_, setStage) => {
38
+ setStage?.('arguments');
39
+ const parsed = parse(JSON.stringify(arguments_));
40
+ setStage?.('execution');
41
+ const result: unknown = await execute(parsed);
42
+ setStage?.('output');
43
+ if (Array.isArray(result)) {
44
+ return result.length > 0 && result.every(isInputContent) ? result : JSON.stringify(result);
45
+ }
46
+ // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Existing schema-tool callbacks return arbitrary application values; adapt them to the Agents dispatcher's output contract.
47
+ if (typeof result === 'object' || typeof result === 'string') {
48
+ return result;
49
+ }
50
+ const text = result === undefined ? 'undefined' : JSON.stringify(result);
51
+ if (text === undefined) {
52
+ throw new OpenAIError('Tool output must be JSON serializable');
53
+ }
54
+ return text;
55
+ };
56
+ Object.defineProperty(handler, toolStages, { value: true });
35
57
  return {
36
58
  name: tool.name,
37
59
  definition: {
@@ -41,20 +63,6 @@ export function functionTool<Arguments>(
41
63
  parameters: tool.parameters,
42
64
  ...(tool.defer_loading === undefined ? {} : { defer_loading: tool.defer_loading }),
43
65
  },
44
- handler: async (arguments_) => {
45
- const result: unknown = await execute(parse(JSON.stringify(arguments_)));
46
- if (Array.isArray(result)) {
47
- return result.length > 0 && result.every(isInputContent) ? result : JSON.stringify(result);
48
- }
49
- // oxlint-disable-next-line anti-slop/no-runtime-typeof -- Existing schema-tool callbacks return arbitrary application values; adapt them to the Agents dispatcher's output contract.
50
- if (typeof result === 'object' || typeof result === 'string') {
51
- return result;
52
- }
53
- const text = result === undefined ? 'undefined' : JSON.stringify(result);
54
- if (text === undefined) {
55
- throw new OpenAIError('Tool output must be JSON serializable');
56
- }
57
- return text;
58
- },
66
+ handler,
59
67
  };
60
68
  }
@@ -0,0 +1,226 @@
1
+ import { APIUserAbortError, BadRequestError, OpenAIError } from '../../../core/error';
2
+ import { buildHeaders } from '../../../internal/headers';
3
+ import type { RequestOptions } from '../../../internal/request-options';
4
+ import { uuid4 } from '../../../internal/utils/uuid';
5
+ import { isObj } from '../../../internal/utils/values';
6
+ import { isInputContent } from './tool-output';
7
+ import type { AgentToolError } from './tool-error';
8
+ import { toolStages } from './tool-stages';
9
+ import type { StagedToolHandler } from './tool-stages';
10
+ import type {
11
+ AgentToolHandler,
12
+ AgentToolOutput,
13
+ AgentSessionStreamParams,
14
+ } from '../../agents/agent-session-stream';
15
+ import type {
16
+ AgentFunctionCallItem,
17
+ AgentFunctionCallOutputParam,
18
+ AgentSessionInputParam,
19
+ } from '../../../resources/beta/agents/agents';
20
+ import type { Sessions } from '../../../resources/beta/agents/sessions/sessions';
21
+
22
+ type ToolResult = AgentSessionInputParam.SessionInputParamAgentSessionInputToolResult;
23
+
24
+ function normalizedOutput(value: unknown): AgentFunctionCallOutputParam | null {
25
+ if (value === null) {
26
+ return null;
27
+ }
28
+ if (typeof value === 'string' || (Array.isArray(value) && value.every(isInputContent))) {
29
+ return value;
30
+ }
31
+ throw new OpenAIError('Tool output must be text, content, a JSON object, or null');
32
+ }
33
+
34
+ // oxlint-disable-next-line anti-slop/no-object-parameters -- The public AgentToolOutput contract accepts arbitrary JSON-serializable object results.
35
+ function toolResult(call: AgentFunctionCallItem, value: AgentToolOutput): ToolResult {
36
+ const output = isObj(value) ? JSON.stringify(value) : value;
37
+ // Detect unserializable callback results inside the redacted failure boundary.
38
+ const serialized = JSON.stringify(output);
39
+ if (serialized === undefined) {
40
+ throw new OpenAIError('Tool output must be JSON serializable');
41
+ }
42
+ return {
43
+ type: 'agent.session.input.tool_result',
44
+ turn_id: call.turn_id,
45
+ call_id: call.call_id,
46
+ success: true,
47
+ output: normalizedOutput(typeof output === 'string' ? output : JSON.parse(serialized)),
48
+ };
49
+ }
50
+
51
+ /** Shared local function dispatch for creation and follow-up streams. @internal */
52
+ export class AgentToolDispatcher {
53
+ readonly #handlers: Map<string, AgentToolHandler>;
54
+ readonly #options: RequestOptions;
55
+ readonly #sessions: Sessions;
56
+ readonly #controller: AbortController;
57
+ readonly #onToolError: AgentSessionStreamParams['onToolError'];
58
+
59
+ constructor(
60
+ sessions: Sessions,
61
+ handlers: Record<string, AgentToolHandler>,
62
+ controller: AbortController,
63
+ options?: RequestOptions,
64
+ onToolError?: AgentSessionStreamParams['onToolError'],
65
+ ) {
66
+ this.#sessions = sessions;
67
+ this.#controller = controller;
68
+ this.#onToolError = onToolError;
69
+ this.#handlers = new Map(Object.entries(handlers));
70
+ const headers = buildHeaders([options?.headers]);
71
+ headers.values.delete('idempotency-key');
72
+ headers.nulls.delete('idempotency-key');
73
+ const { idempotencyKey: _key, ...rest } = options ?? {};
74
+ this.#options = { ...rest, headers, signal: controller.signal };
75
+ }
76
+
77
+ canHandle(name: string): boolean {
78
+ return typeof this.#handlers.get(name) === 'function';
79
+ }
80
+
81
+ /** Capture routing and arguments before the caller can mutate the yielded event. */
82
+ prepare(
83
+ call: AgentFunctionCallItem | undefined,
84
+ sessionID: string | undefined,
85
+ ): (() => Promise<void>) | undefined {
86
+ const handler = call && this.#handlers.get(call.name);
87
+ if (!call || typeof handler !== 'function') {
88
+ return;
89
+ }
90
+ if (!sessionID) {
91
+ throw new OpenAIError('Tool call received before the session creation event');
92
+ }
93
+ const snapshot = structuredClone(call);
94
+ return async () => {
95
+ this.#checkAbort();
96
+ const result = await this.#result(snapshot, handler, sessionID);
97
+ this.#checkAbort();
98
+ await this.#submit(sessionID, result);
99
+ };
100
+ }
101
+
102
+ async #result(
103
+ call: AgentFunctionCallItem,
104
+ handler: StagedToolHandler,
105
+ sessionID: string,
106
+ ): Promise<ToolResult> {
107
+ let stage: AgentToolError['stage'] = 'arguments';
108
+ try {
109
+ const args: unknown = typeof call.arguments === 'string' ? JSON.parse(call.arguments) : call.arguments;
110
+ if (!isObj(args)) {
111
+ throw new OpenAIError('Function arguments must be a JSON object');
112
+ }
113
+ stage = 'execution';
114
+ // SAFETY: Arguments were parsed as JSON and checked to be a non-null non-array object before invoking the handler.
115
+ const arguments_ = args as Record<string, unknown>;
116
+ const output = await this.#wait(() =>
117
+ this.#onToolError && handler[toolStages]
118
+ ? handler(arguments_, (value) => {
119
+ stage = value;
120
+ })
121
+ : handler(arguments_),
122
+ );
123
+ stage = 'output';
124
+ return toolResult(call, output);
125
+ } catch (error) {
126
+ this.#checkAbort();
127
+ if (this.#onToolError) {
128
+ const failure: AgentToolError = Object.freeze({
129
+ error,
130
+ stage,
131
+ tool_name: call.name,
132
+ session_id: sessionID,
133
+ turn_id: call.turn_id,
134
+ call_id: call.call_id,
135
+ });
136
+ try {
137
+ await this.#wait(() => this.#onToolError?.(failure));
138
+ } catch {
139
+ // Observers must not prevent the original sanitized tool failure from being submitted.
140
+ this.#checkAbort();
141
+ }
142
+ }
143
+ return {
144
+ type: 'agent.session.input.tool_result',
145
+ turn_id: call.turn_id,
146
+ call_id: call.call_id,
147
+ success: false,
148
+ error: 'Tool handler failed.',
149
+ };
150
+ }
151
+ }
152
+
153
+ #checkAbort(): void {
154
+ if (this.#controller.signal.aborted) {
155
+ throw this.#abortError();
156
+ }
157
+ }
158
+
159
+ #abortError(): APIUserAbortError {
160
+ const error = new APIUserAbortError();
161
+ Object.defineProperty(error, 'cause', {
162
+ value: this.#controller.signal.reason,
163
+ writable: true,
164
+ configurable: true,
165
+ });
166
+ return error;
167
+ }
168
+
169
+ async #wait<Value>(action: () => Value | PromiseLike<Value>): Promise<Value> {
170
+ let onAbort: (() => void) | undefined;
171
+ // oxlint-disable-next-line promise/avoid-new -- Bridge the caller's AbortSignal while a handler or registration delay is pending.
172
+ const aborted = new Promise<never>((_resolve, reject) => {
173
+ onAbort = () => reject(this.#abortError());
174
+ this.#controller.signal.addEventListener('abort', onAbort, { once: true });
175
+ });
176
+ try {
177
+ this.#checkAbort();
178
+ // Capture synchronous throws before racing cancellation, so both promises
179
+ // always have rejection handlers even if the callback aborts and throws.
180
+ const invoke = async () => await action();
181
+ return await Promise.race([invoke(), aborted]);
182
+ } finally {
183
+ if (onAbort) {
184
+ this.#controller.signal.removeEventListener('abort', onAbort);
185
+ }
186
+ }
187
+ }
188
+
189
+ async #submit(sessionID: string, result: ToolResult, key = uuid4(), attempt = 0): Promise<void> {
190
+ this.#checkAbort();
191
+ try {
192
+ await this.#sessions.events.create(
193
+ sessionID,
194
+ { events: [result], 'Idempotency-Key': key },
195
+ this.#options,
196
+ );
197
+ } catch (error) {
198
+ const delay = [100, 300, 600][attempt];
199
+ if (
200
+ delay === undefined ||
201
+ !(error instanceof BadRequestError) ||
202
+ error.code !== 'invalid_request_error' ||
203
+ !error.error ||
204
+ !('message' in error.error) ||
205
+ error.error.message !== `Unknown pending tool call: ${result.call_id}`
206
+ ) {
207
+ throw error;
208
+ }
209
+ let timer: ReturnType<typeof setTimeout> | undefined;
210
+ try {
211
+ await this.#wait(
212
+ () =>
213
+ // oxlint-disable-next-line promise/avoid-new -- Own the registration timer so cancellation clears it promptly.
214
+ new Promise<void>((resolve) => {
215
+ timer = setTimeout(resolve, delay);
216
+ }),
217
+ );
218
+ } finally {
219
+ if (timer !== undefined) {
220
+ clearTimeout(timer);
221
+ }
222
+ }
223
+ await this.#submit(sessionID, result, key, attempt + 1);
224
+ }
225
+ }
226
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Beta diagnostic for logging or monitoring failures from local `toolHandlers`.
3
+ * The original error is local only; API and transport failures propagate separately.
4
+ */
5
+ export interface AgentToolError {
6
+ readonly error: unknown;
7
+ readonly tool_name: string;
8
+ readonly session_id: string;
9
+ readonly turn_id: string;
10
+ readonly call_id: string;
11
+ readonly stage: 'arguments' | 'execution' | 'output';
12
+ }
@@ -0,0 +1,14 @@
1
+ import type { AgentToolHandler } from '../../agents/agent-session-stream';
2
+ import type { AgentToolError } from './tool-error';
3
+
4
+ /** @internal */
5
+ export const toolStages = Symbol.for('openai.beta.agents.toolStages');
6
+
7
+ /**
8
+ * Marks handlers that accept stage updates, including across ESM/CJS imports.
9
+ * @internal
10
+ */
11
+ export type StagedToolHandler = ((
12
+ arguments_: Parameters<AgentToolHandler>[0],
13
+ setStage?: (stage: AgentToolError['stage']) => void,
14
+ ) => ReturnType<AgentToolHandler>) & { [toolStages]?: true };
@@ -0,0 +1,35 @@
1
+ import type { Stream } from '../../core/streaming';
2
+ import type { ChatCompletionChunk } from '../../resources/chat/completions/completions';
3
+
4
+ /** Wire chunks can omit the finish reason, including the final audio expiry update. */
5
+ export type ChatCompletionWireChunk = Omit<ChatCompletionChunk, 'choices'> & {
6
+ choices: (Omit<ChatCompletionChunk.Choice, 'finish_reason'> & {
7
+ finish_reason?: ChatCompletionChunk.Choice['finish_reason'];
8
+ })[];
9
+ };
10
+
11
+ export function normalizeChatCompletionChunk(chunk: ChatCompletionWireChunk): ChatCompletionChunk {
12
+ if (!Array.isArray(chunk?.choices)) {
13
+ // SAFETY: This is an opaque provider record, with no choices to normalize.
14
+ // Preserve the existing SSE pass-through; the SDK does not validate these records.
15
+ return chunk as ChatCompletionChunk;
16
+ }
17
+ return {
18
+ ...chunk,
19
+ choices: chunk.choices.map((choice) => ({ ...choice, finish_reason: choice.finish_reason ?? null })),
20
+ };
21
+ }
22
+
23
+ export function normalizeChatCompletionStream(
24
+ stream: Stream<ChatCompletionWireChunk>,
25
+ ): Stream<ChatCompletionChunk> {
26
+ const iterator = stream[Symbol.asyncIterator].bind(stream);
27
+ stream[Symbol.asyncIterator] = async function* normalizedChunks() {
28
+ const source = { [Symbol.asyncIterator]: iterator };
29
+ for await (const chunk of source) {
30
+ yield normalizeChatCompletionChunk(chunk);
31
+ }
32
+ };
33
+ // SAFETY: The installed iterator normalizes every wire chunk before exposing it.
34
+ return stream as Stream<ChatCompletionChunk>;
35
+ }