@effect/ai-openai 4.0.0-beta.6 → 4.0.0-beta.60

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 (52) hide show
  1. package/dist/Generated.d.ts +66011 -38686
  2. package/dist/Generated.d.ts.map +1 -1
  3. package/dist/Generated.js +1 -1
  4. package/dist/Generated.js.map +1 -1
  5. package/dist/OpenAiClient.d.ts +63 -17
  6. package/dist/OpenAiClient.d.ts.map +1 -1
  7. package/dist/OpenAiClient.js +210 -33
  8. package/dist/OpenAiClient.js.map +1 -1
  9. package/dist/OpenAiClientGenerated.d.ts +91 -0
  10. package/dist/OpenAiClientGenerated.d.ts.map +1 -0
  11. package/dist/OpenAiClientGenerated.js +84 -0
  12. package/dist/OpenAiClientGenerated.js.map +1 -0
  13. package/dist/OpenAiConfig.d.ts +2 -2
  14. package/dist/OpenAiConfig.d.ts.map +1 -1
  15. package/dist/OpenAiConfig.js +3 -3
  16. package/dist/OpenAiConfig.js.map +1 -1
  17. package/dist/OpenAiEmbeddingModel.d.ts +85 -0
  18. package/dist/OpenAiEmbeddingModel.d.ts.map +1 -0
  19. package/dist/OpenAiEmbeddingModel.js +119 -0
  20. package/dist/OpenAiEmbeddingModel.js.map +1 -0
  21. package/dist/OpenAiError.d.ts +22 -32
  22. package/dist/OpenAiError.d.ts.map +1 -1
  23. package/dist/OpenAiLanguageModel.d.ts +43 -49
  24. package/dist/OpenAiLanguageModel.d.ts.map +1 -1
  25. package/dist/OpenAiLanguageModel.js +296 -152
  26. package/dist/OpenAiLanguageModel.js.map +1 -1
  27. package/dist/OpenAiSchema.d.ts +1920 -0
  28. package/dist/OpenAiSchema.d.ts.map +1 -0
  29. package/dist/OpenAiSchema.js +536 -0
  30. package/dist/OpenAiSchema.js.map +1 -0
  31. package/dist/OpenAiTool.d.ts +8 -7
  32. package/dist/OpenAiTool.d.ts.map +1 -1
  33. package/dist/OpenAiTool.js +2 -1
  34. package/dist/OpenAiTool.js.map +1 -1
  35. package/dist/index.d.ts +18 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +18 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/internal/errors.js +4 -4
  40. package/dist/internal/errors.js.map +1 -1
  41. package/package.json +3 -3
  42. package/src/Generated.ts +7416 -4257
  43. package/src/OpenAiClient.ts +377 -81
  44. package/src/OpenAiClientGenerated.ts +202 -0
  45. package/src/OpenAiConfig.ts +3 -3
  46. package/src/OpenAiEmbeddingModel.ts +203 -0
  47. package/src/OpenAiError.ts +24 -32
  48. package/src/OpenAiLanguageModel.ts +420 -144
  49. package/src/OpenAiSchema.ts +875 -0
  50. package/src/OpenAiTool.ts +2 -1
  51. package/src/index.ts +21 -0
  52. package/src/internal/errors.ts +6 -4
@@ -6,21 +6,23 @@
6
6
  *
7
7
  * @since 1.0.0
8
8
  */
9
+ import * as Context from "effect/Context";
9
10
  import * as DateTime from "effect/DateTime";
10
11
  import * as Effect from "effect/Effect";
11
12
  import * as Encoding from "effect/Encoding";
12
13
  import { dual } from "effect/Function";
13
14
  import * as Layer from "effect/Layer";
15
+ import * as Option from "effect/Option";
14
16
  import * as Predicate from "effect/Predicate";
15
17
  import * as Redactable from "effect/Redactable";
16
18
  import * as Schema from "effect/Schema";
17
19
  import * as AST from "effect/SchemaAST";
18
- import * as ServiceMap from "effect/ServiceMap";
19
20
  import * as Stream from "effect/Stream";
20
21
  import * as AiError from "effect/unstable/ai/AiError";
21
22
  import * as IdGenerator from "effect/unstable/ai/IdGenerator";
22
23
  import * as LanguageModel from "effect/unstable/ai/LanguageModel";
23
24
  import * as AiModel from "effect/unstable/ai/Model";
25
+ import { toCodecOpenAI } from "effect/unstable/ai/OpenAiStructuredOutput";
24
26
  import * as Tool from "effect/unstable/ai/Tool";
25
27
  import * as Generated from "./Generated.js";
26
28
  import * as InternalUtilities from "./internal/utilities.js";
@@ -37,7 +39,7 @@ const SharedModelIds = Generated.ModelIdsShared.members[1];
37
39
  * @since 1.0.0
38
40
  * @category services
39
41
  */
40
- export class Config extends /*#__PURE__*/ServiceMap.Service()("@effect/ai-openai/OpenAiLanguageModel/Config") {}
42
+ export class Config extends /*#__PURE__*/Context.Service()("@effect/ai-openai/OpenAiLanguageModel/Config") {}
41
43
  // =============================================================================
42
44
  // Language Model
43
45
  // =============================================================================
@@ -45,7 +47,7 @@ export class Config extends /*#__PURE__*/ServiceMap.Service()("@effect/ai-openai
45
47
  * @since 1.0.0
46
48
  * @category constructors
47
49
  */
48
- export const model = (model, config) => AiModel.make("openai", layer({
50
+ export const model = (model, config) => AiModel.make("openai", model, layer({
49
51
  model,
50
52
  config
51
53
  }));
@@ -58,7 +60,7 @@ export const model = (model, config) => AiModel.make("openai", layer({
58
60
  // model: (string & {}) | Model,
59
61
  // config?: Omit<typeof Config.Service, "model">
60
62
  // ): AiModel.Model<"openai", LanguageModel.LanguageModel | Tokenizer.Tokenizer, OpenAiClient> =>
61
- // AiModel.make("openai", layerWithTokenizer({ model, config }))
63
+ // AiModel.make("openai", model, layerWithTokenizer({ model, config }))
62
64
  /**
63
65
  * Creates an OpenAI language model service.
64
66
  *
@@ -71,7 +73,7 @@ export const make = /*#__PURE__*/Effect.fnUntraced(function* ({
71
73
  }) {
72
74
  const client = yield* OpenAiClient;
73
75
  const makeConfig = Effect.gen(function* () {
74
- const services = yield* Effect.services();
76
+ const services = yield* Effect.context();
75
77
  return {
76
78
  model,
77
79
  ...providerConfig,
@@ -100,28 +102,26 @@ export const make = /*#__PURE__*/Effect.fnUntraced(function* ({
100
102
  options,
101
103
  toolNameMapper
102
104
  });
103
- const responseFormat = prepareResponseFormat({
105
+ const responseFormat = yield* prepareResponseFormat({
104
106
  config,
105
107
  options
106
108
  });
107
109
  const request = {
108
110
  ...config,
109
111
  input: messages,
110
- include: include.size > 0 ? Array.from(include) : null,
112
+ include: include.size > 0 ? Array.from(include) : undefined,
111
113
  text: {
112
- verbosity: config.text?.verbosity ?? null,
114
+ verbosity: config.text?.verbosity ?? undefined,
113
115
  format: responseFormat
114
- },
115
- ...(Predicate.isNotUndefined(tools) ? {
116
- tools
117
- } : undefined),
118
- ...(Predicate.isNotUndefined(toolChoice) ? {
119
- tool_choice: toolChoice
120
- } : undefined)
116
+ }
121
117
  };
118
+ if (tools) request.tools = tools;
119
+ if (toolChoice) request.tool_choice = toolChoice;
120
+ if (options.previousResponseId) request.previous_response_id = options.previousResponseId;
122
121
  return request;
123
122
  });
124
123
  return yield* LanguageModel.make({
124
+ codecTransformer: toCodecOpenAI,
125
125
  generateText: Effect.fnUntraced(function* (options) {
126
126
  const config = yield* makeConfig;
127
127
  const toolNameMapper = new Tool.NameMapper(options.tools);
@@ -207,14 +207,15 @@ const prepareMessages = /*#__PURE__*/Effect.fnUntraced(function* ({
207
207
  if (config.store === false && capabilities.isReasoningModel) {
208
208
  include.add("reasoning.encrypted_content");
209
209
  }
210
- if (Predicate.isNotUndefined(codeInterpreterTool)) {
210
+ if (codeInterpreterTool) {
211
211
  include.add("code_interpreter_call.outputs");
212
212
  }
213
- if (Predicate.isNotUndefined(webSearchTool) || Predicate.isNotUndefined(webSearchPreviewTool)) {
213
+ if (webSearchTool || webSearchPreviewTool) {
214
214
  include.add("web_search_call.action.sources");
215
215
  }
216
216
  const messages = [];
217
- for (const message of options.prompt.content) {
217
+ const prompt = options.incrementalPrompt ?? options.prompt;
218
+ for (const message of prompt.content) {
218
219
  switch (message.role) {
219
220
  case "system":
220
221
  {
@@ -378,7 +379,9 @@ const prepareMessages = /*#__PURE__*/Effect.fnUntraced(function* ({
378
379
  type: "reasoning",
379
380
  id,
380
381
  summary: summaryParts,
381
- encrypted_content: encryptedContent ?? null
382
+ ...(Predicate.isNotNull(encryptedContent) ? {
383
+ encrypted_content: encryptedContent
384
+ } : undefined)
382
385
  };
383
386
  messages.push(reasoningMessages[id]);
384
387
  } else {
@@ -569,13 +572,15 @@ const buildHttpRequestDetails = request => ({
569
572
  method: request.method,
570
573
  url: request.url,
571
574
  urlParams: Array.from(request.urlParams),
572
- hash: request.hash,
575
+ hash: Option.getOrUndefined(request.hash),
573
576
  headers: Redactable.redact(request.headers)
574
577
  });
575
578
  const buildHttpResponseDetails = response => ({
576
579
  status: response.status,
577
580
  headers: Redactable.redact(response.headers)
578
581
  });
582
+ const knownResponseStreamEventTypes = /*#__PURE__*/new Set(["response.created", "response.completed", "response.incomplete", "response.failed", "response.output_item.added", "response.output_item.done", "response.output_text.delta", "response.output_text.annotation.added", "response.reasoning_summary_part.added", "response.reasoning_summary_part.done", "response.reasoning_summary_text.delta", "response.function_call_arguments.delta", "response.function_call_arguments.done", "response.code_interpreter_call_code.delta", "response.code_interpreter_call_code.done", "response.apply_patch_call_operation_diff.delta", "response.apply_patch_call_operation_diff.done", "response.image_generation_call.partial_image", "error"]);
583
+ const isKnownResponseStreamEvent = event => knownResponseStreamEventTypes.has(event.type);
579
584
  const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
580
585
  options,
581
586
  rawResponse,
@@ -609,9 +614,7 @@ const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
609
614
  operation: part.operation
610
615
  },
611
616
  metadata: {
612
- openai: {
613
- ...makeItemIdMetadata(part.id)
614
- }
617
+ openai: makeItemIdMetadata(part.id)
615
618
  }
616
619
  });
617
620
  break;
@@ -669,9 +672,8 @@ const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
669
672
  {
670
673
  hasToolCalls = true;
671
674
  const toolName = part.name;
672
- const toolParams = part.arguments;
673
- const params = yield* Effect.try({
674
- try: () => Tool.unsafeSecureJsonParse(toolParams),
675
+ const toolParams = yield* Effect.try({
676
+ try: () => Tool.unsafeSecureJsonParse(part.arguments),
675
677
  catch: cause => AiError.make({
676
678
  module: "OpenAiLanguageModel",
677
679
  method: "makeResponse",
@@ -682,15 +684,14 @@ const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
682
684
  })
683
685
  })
684
686
  });
687
+ const params = yield* transformToolCallParams(options.tools, part.name, toolParams);
685
688
  parts.push({
686
689
  type: "tool-call",
687
690
  id: part.call_id,
688
691
  name: toolName,
689
692
  params,
690
693
  metadata: {
691
- openai: {
692
- ...makeItemIdMetadata(part.id)
693
- }
694
+ openai: makeItemIdMetadata(part.id)
694
695
  }
695
696
  });
696
697
  break;
@@ -727,9 +728,7 @@ const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
727
728
  action: part.action
728
729
  },
729
730
  metadata: {
730
- openai: {
731
- ...makeItemIdMetadata(part.id)
732
- }
731
+ openai: makeItemIdMetadata(part.id)
733
732
  }
734
733
  });
735
734
  break;
@@ -737,12 +736,19 @@ const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
737
736
  case "mcp_call":
738
737
  {
739
738
  const toolId = Predicate.isNotNullish(part.approval_request_id) ? approvalRequests.get(part.approval_request_id) ?? part.id : part.id;
740
- const toolName = `mcp.${part.name}`;
739
+ const {
740
+ toolName,
741
+ params
742
+ } = yield* normalizeMcpToolCall({
743
+ toolNameMapper,
744
+ toolParams: part.arguments,
745
+ method: "makeResponse"
746
+ });
741
747
  parts.push({
742
748
  type: "tool-call",
743
749
  id: toolId,
744
750
  name: toolName,
745
- params: part.arguments,
751
+ params,
746
752
  providerExecuted: true
747
753
  });
748
754
  parts.push({
@@ -752,7 +758,7 @@ const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
752
758
  isFailure: false,
753
759
  providerExecuted: true,
754
760
  result: {
755
- type: "call",
761
+ type: "mcp_call",
756
762
  name: part.name,
757
763
  arguments: part.arguments,
758
764
  server_label: part.server_label,
@@ -764,9 +770,7 @@ const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
764
770
  } : undefined)
765
771
  },
766
772
  metadata: {
767
- openai: {
768
- ...makeItemIdMetadata(part.id)
769
- }
773
+ openai: makeItemIdMetadata(part.id)
770
774
  }
771
775
  });
772
776
  break;
@@ -780,18 +784,13 @@ const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
780
784
  {
781
785
  const approvalRequestId = part.approval_request_id ?? part.id;
782
786
  const toolId = yield* idGenerator.generateId();
783
- const toolName = `mcp.${part.name}`;
784
- const params = yield* Effect.try({
785
- try: () => Tool.unsafeSecureJsonParse(part.arguments),
786
- catch: cause => AiError.make({
787
- module: "OpenAiLanguageModel",
788
- method: "makeResponse",
789
- reason: new AiError.ToolParameterValidationError({
790
- toolName,
791
- toolParams: {},
792
- description: `Failed securely JSON parse tool parameters: ${cause}`
793
- })
794
- })
787
+ const {
788
+ toolName,
789
+ params
790
+ } = yield* normalizeMcpToolCall({
791
+ toolNameMapper,
792
+ toolParams: part.arguments,
793
+ method: "makeResponse"
795
794
  });
796
795
  parts.push({
797
796
  type: "tool-call",
@@ -952,9 +951,7 @@ const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
952
951
  action: part.action
953
952
  },
954
953
  metadata: {
955
- openai: {
956
- ...makeItemIdMetadata(part.id)
957
- }
954
+ openai: makeItemIdMetadata(part.id)
958
955
  }
959
956
  });
960
957
  break;
@@ -990,13 +987,7 @@ const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
990
987
  reason: finishReason,
991
988
  usage: getUsage(rawResponse.usage),
992
989
  response: buildHttpResponseDetails(response),
993
- ...(rawResponse.service_tier && {
994
- metadata: {
995
- openai: {
996
- serviceTier: rawResponse.service_tier
997
- }
998
- }
999
- })
990
+ ...toServiceTier(rawResponse.service_tier)
1000
991
  });
1001
992
  return parts;
1002
993
  });
@@ -1015,11 +1006,29 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1015
1006
  const activeAnnotations = [];
1016
1007
  // Track active reasoning items with state machine for proper concluding logic
1017
1008
  const activeReasoning = {};
1009
+ const getOrCreateReasoningPart = (itemId, encryptedContent) => {
1010
+ const activePart = activeReasoning[itemId];
1011
+ if (Predicate.isNotUndefined(activePart)) {
1012
+ if (Predicate.isNotNullish(encryptedContent)) {
1013
+ activePart.encryptedContent = encryptedContent;
1014
+ }
1015
+ return activePart;
1016
+ }
1017
+ const reasoningPart = {
1018
+ encryptedContent: Predicate.isNotNullish(encryptedContent) ? encryptedContent : undefined,
1019
+ summaryParts: {}
1020
+ };
1021
+ activeReasoning[itemId] = reasoningPart;
1022
+ return reasoningPart;
1023
+ };
1018
1024
  // Track active tool calls with optional provider-specific state
1019
1025
  const activeToolCalls = {};
1020
1026
  const webSearchTool = options.tools.find(tool => Tool.isProviderDefined(tool) && (tool.name === "OpenAiWebSearch" || tool.name === "OpenAiWebSearchPreview"));
1021
1027
  return stream.pipe(Stream.mapEffect(Effect.fnUntraced(function* (event) {
1022
1028
  const parts = [];
1029
+ if (!isKnownResponseStreamEvent(event)) {
1030
+ return parts;
1031
+ }
1023
1032
  switch (event.type) {
1024
1033
  case "response.created":
1025
1034
  {
@@ -1050,13 +1059,7 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1050
1059
  reason: InternalUtilities.resolveFinishReason(event.response.incomplete_details?.reason, hasToolCalls),
1051
1060
  usage: getUsage(event.response.usage),
1052
1061
  response: buildHttpResponseDetails(response),
1053
- ...(event.response.service_tier && {
1054
- metadata: {
1055
- openai: {
1056
- serviceTier: event.response.service_tier
1057
- }
1058
- }
1059
- })
1062
+ ...toServiceTier(event.response.service_tier)
1060
1063
  });
1061
1064
  break;
1062
1065
  }
@@ -1157,7 +1160,10 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1157
1160
  {
1158
1161
  activeToolCalls[event.output_index] = {
1159
1162
  id: event.item.call_id,
1160
- name: event.item.name
1163
+ name: event.item.name,
1164
+ functionCall: {
1165
+ emitted: false
1166
+ }
1161
1167
  };
1162
1168
  parts.push({
1163
1169
  type: "tool-params-start",
@@ -1195,39 +1201,34 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1195
1201
  type: "text-start",
1196
1202
  id: event.item.id,
1197
1203
  metadata: {
1198
- openai: {
1199
- ...makeItemIdMetadata(event.item.id)
1200
- }
1204
+ openai: makeItemIdMetadata(event.item.id)
1201
1205
  }
1202
1206
  });
1203
1207
  break;
1204
1208
  }
1205
1209
  case "reasoning":
1206
1210
  {
1207
- const encryptedContent = event.item.encrypted_content ?? undefined;
1208
- activeReasoning[event.item.id] = {
1209
- encryptedContent,
1210
- summaryParts: {
1211
- 0: "active"
1212
- }
1213
- };
1214
- parts.push({
1215
- type: "reasoning-start",
1216
- id: `${event.item.id}:0`,
1217
- metadata: {
1218
- openai: {
1219
- ...makeItemIdMetadata(event.item.id),
1220
- ...makeEncryptedContentMetadata(event.item.encrypted_content)
1211
+ const reasoningPart = getOrCreateReasoningPart(event.item.id, event.item.encrypted_content);
1212
+ if (Predicate.isUndefined(reasoningPart.summaryParts[0])) {
1213
+ reasoningPart.summaryParts[0] = "active";
1214
+ parts.push({
1215
+ type: "reasoning-start",
1216
+ id: `${event.item.id}:0`,
1217
+ metadata: {
1218
+ openai: {
1219
+ ...makeItemIdMetadata(event.item.id),
1220
+ ...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
1221
+ }
1221
1222
  }
1222
- }
1223
- });
1223
+ });
1224
+ }
1224
1225
  break;
1225
1226
  }
1226
1227
  case "shell_call":
1227
1228
  {
1228
1229
  const toolName = toolNameMapper.getCustomName("shell");
1229
1230
  activeToolCalls[event.output_index] = {
1230
- id: event.item.id,
1231
+ id: event.item.id ?? event.item.call_id,
1231
1232
  name: toolName
1232
1233
  };
1233
1234
  break;
@@ -1272,7 +1273,7 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1272
1273
  parts.push({
1273
1274
  type: "tool-params-delta",
1274
1275
  id: toolCall.id,
1275
- delta: InternalUtilities.escapeJSONDelta(event.item.operation.diff)
1276
+ delta: InternalUtilities.escapeJSONDelta(event.item.operation.diff ?? "")
1276
1277
  });
1277
1278
  }
1278
1279
  parts.push({
@@ -1298,9 +1299,7 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1298
1299
  operation: event.item.operation
1299
1300
  },
1300
1301
  metadata: {
1301
- openai: {
1302
- ...makeItemIdMetadata(event.item.id)
1303
- }
1302
+ openai: makeItemIdMetadata(event.item.id)
1304
1303
  }
1305
1304
  });
1306
1305
  }
@@ -1372,12 +1371,17 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1372
1371
  }
1373
1372
  case "function_call":
1374
1373
  {
1374
+ const toolCall = activeToolCalls[event.output_index];
1375
+ if (Predicate.isNotUndefined(toolCall?.functionCall?.emitted) && toolCall.functionCall.emitted) {
1376
+ delete activeToolCalls[event.output_index];
1377
+ break;
1378
+ }
1375
1379
  delete activeToolCalls[event.output_index];
1376
1380
  hasToolCalls = true;
1377
1381
  const toolName = event.item.name;
1378
- const toolParams = event.item.arguments;
1379
- const params = yield* Effect.try({
1380
- try: () => Tool.unsafeSecureJsonParse(toolParams),
1382
+ const toolArgs = event.item.arguments;
1383
+ const toolParams = yield* Effect.try({
1384
+ try: () => Tool.unsafeSecureJsonParse(toolArgs),
1381
1385
  catch: cause => AiError.make({
1382
1386
  module: "OpenAiLanguageModel",
1383
1387
  method: "makeStreamResponse",
@@ -1388,6 +1392,7 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1388
1392
  })
1389
1393
  })
1390
1394
  });
1395
+ const params = yield* transformToolCallParams(options.tools, toolName, toolParams);
1391
1396
  parts.push({
1392
1397
  type: "tool-params-end",
1393
1398
  id: event.item.call_id
@@ -1398,9 +1403,7 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1398
1403
  name: toolName,
1399
1404
  params,
1400
1405
  metadata: {
1401
- openai: {
1402
- ...makeItemIdMetadata(event.item.id)
1403
- }
1406
+ openai: makeItemIdMetadata(event.item.id)
1404
1407
  }
1405
1408
  });
1406
1409
  break;
@@ -1431,9 +1434,7 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1431
1434
  action: event.item.action
1432
1435
  },
1433
1436
  metadata: {
1434
- openai: {
1435
- ...makeItemIdMetadata(event.item.id)
1436
- }
1437
+ openai: makeItemIdMetadata(event.item.id)
1437
1438
  }
1438
1439
  });
1439
1440
  break;
@@ -1443,12 +1444,19 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1443
1444
  const approvalRequestId = event.item.approval_request_id;
1444
1445
  // Track approval with our own tool call identifiers
1445
1446
  const toolId = Predicate.isNotNullish(approvalRequestId) ? streamApprovalRequests.get(approvalRequestId) ?? approvalRequests.get(approvalRequestId) ?? event.item.id : event.item.id;
1446
- const toolName = `mcp.${event.item.name}`;
1447
+ const {
1448
+ toolName,
1449
+ params
1450
+ } = yield* normalizeMcpToolCall({
1451
+ toolNameMapper,
1452
+ toolParams: event.item.arguments,
1453
+ method: "makeStreamResponse"
1454
+ });
1447
1455
  parts.push({
1448
1456
  type: "tool-call",
1449
1457
  id: toolId,
1450
1458
  name: toolName,
1451
- params: event.item.arguments,
1459
+ params,
1452
1460
  providerExecuted: true
1453
1461
  });
1454
1462
  parts.push({
@@ -1458,7 +1466,7 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1458
1466
  isFailure: false,
1459
1467
  providerExecuted: true,
1460
1468
  result: {
1461
- type: "call",
1469
+ type: "mcp_call",
1462
1470
  name: event.item.name,
1463
1471
  arguments: event.item.arguments,
1464
1472
  server_label: event.item.server_label,
@@ -1470,9 +1478,7 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1470
1478
  } : undefined)
1471
1479
  },
1472
1480
  metadata: {
1473
- openai: {
1474
- ...makeItemIdMetadata(event.item.id)
1475
- }
1481
+ openai: makeItemIdMetadata(event.item.id)
1476
1482
  }
1477
1483
  });
1478
1484
  break;
@@ -1487,12 +1493,19 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1487
1493
  const toolId = yield* idGenerator.generateId();
1488
1494
  const approvalRequestId = event.item.approval_request_id ?? event.item.id;
1489
1495
  streamApprovalRequests.set(approvalRequestId, toolId);
1490
- const toolName = `mcp.${event.item.name}`;
1496
+ const {
1497
+ toolName,
1498
+ params
1499
+ } = yield* normalizeMcpToolCall({
1500
+ toolNameMapper,
1501
+ toolParams: event.item.arguments,
1502
+ method: "makeStreamResponse"
1503
+ });
1491
1504
  parts.push({
1492
1505
  type: "tool-call",
1493
1506
  id: toolId,
1494
1507
  name: toolName,
1495
- params: event.item.arguments,
1508
+ params,
1496
1509
  providerExecuted: true
1497
1510
  });
1498
1511
  parts.push({
@@ -1521,7 +1534,7 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1521
1534
  }
1522
1535
  case "reasoning":
1523
1536
  {
1524
- const reasoningPart = activeReasoning[event.item.id];
1537
+ const reasoningPart = getOrCreateReasoningPart(event.item.id, event.item.encrypted_content);
1525
1538
  for (const [summaryIndex, status] of Object.entries(reasoningPart.summaryParts)) {
1526
1539
  if (status === "active" || status === "can-conclude") {
1527
1540
  parts.push({
@@ -1530,7 +1543,7 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1530
1543
  metadata: {
1531
1544
  openai: {
1532
1545
  ...makeItemIdMetadata(event.item.id),
1533
- ...makeEncryptedContentMetadata(event.item.encrypted_content)
1546
+ ...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
1534
1547
  }
1535
1548
  }
1536
1549
  });
@@ -1545,15 +1558,13 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1545
1558
  const toolName = toolNameMapper.getCustomName("shell");
1546
1559
  parts.push({
1547
1560
  type: "tool-call",
1548
- id: event.item.id,
1561
+ id: event.item.id ?? event.item.call_id,
1549
1562
  name: toolName,
1550
1563
  params: {
1551
1564
  action: event.item.action
1552
1565
  },
1553
1566
  metadata: {
1554
- openai: {
1555
- ...makeItemIdMetadata(event.item.id)
1556
- }
1567
+ openai: makeItemIdMetadata(event.item.id)
1557
1568
  }
1558
1569
  });
1559
1570
  break;
@@ -1670,6 +1681,41 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1670
1681
  }
1671
1682
  break;
1672
1683
  }
1684
+ case "response.function_call_arguments.done":
1685
+ {
1686
+ const toolCall = activeToolCalls[event.output_index];
1687
+ if (Predicate.isNotUndefined(toolCall?.functionCall) && !toolCall.functionCall.emitted) {
1688
+ hasToolCalls = true;
1689
+ const toolParams = yield* Effect.try({
1690
+ try: () => Tool.unsafeSecureJsonParse(event.arguments),
1691
+ catch: cause => AiError.make({
1692
+ module: "OpenAiLanguageModel",
1693
+ method: "makeStreamResponse",
1694
+ reason: new AiError.ToolParameterValidationError({
1695
+ toolName: toolCall.name,
1696
+ toolParams: {},
1697
+ description: `Failed securely JSON parse tool parameters: ${cause}`
1698
+ })
1699
+ })
1700
+ });
1701
+ const params = yield* transformToolCallParams(options.tools, toolCall.name, toolParams);
1702
+ parts.push({
1703
+ type: "tool-params-end",
1704
+ id: toolCall.id
1705
+ });
1706
+ parts.push({
1707
+ type: "tool-call",
1708
+ id: toolCall.id,
1709
+ name: toolCall.name,
1710
+ params,
1711
+ metadata: {
1712
+ openai: makeItemIdMetadata(event.item_id)
1713
+ }
1714
+ });
1715
+ toolCall.functionCall.emitted = true;
1716
+ }
1717
+ break;
1718
+ }
1673
1719
  case "response.apply_patch_call_operation_diff.delta":
1674
1720
  {
1675
1721
  const toolCall = activeToolCalls[event.output_index];
@@ -1765,28 +1811,27 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1765
1811
  }
1766
1812
  case "response.reasoning_summary_part.added":
1767
1813
  {
1768
- // The first reasoning start is pushed in the `response.output_item.added` block
1814
+ const reasoningPart = getOrCreateReasoningPart(event.item_id);
1769
1815
  if (event.summary_index > 0) {
1770
- const reasoningPart = activeReasoning[event.item_id];
1771
- if (Predicate.isNotUndefined(reasoningPart)) {
1772
- // Conclude all can-conclude parts before starting new one
1773
- for (const [summaryIndex, status] of Object.entries(reasoningPart.summaryParts)) {
1774
- if (status === "can-conclude") {
1775
- parts.push({
1776
- type: "reasoning-end",
1777
- id: `${event.item_id}:${summaryIndex}`,
1778
- metadata: {
1779
- openai: {
1780
- ...makeItemIdMetadata(event.item_id),
1781
- ...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
1782
- }
1816
+ // Conclude all can-conclude parts before starting new one
1817
+ for (const [summaryIndex, status] of Object.entries(reasoningPart.summaryParts)) {
1818
+ if (status === "can-conclude") {
1819
+ parts.push({
1820
+ type: "reasoning-end",
1821
+ id: `${event.item_id}:${summaryIndex}`,
1822
+ metadata: {
1823
+ openai: {
1824
+ ...makeItemIdMetadata(event.item_id),
1825
+ ...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
1783
1826
  }
1784
- });
1785
- reasoningPart.summaryParts[Number(summaryIndex)] = "concluded";
1786
- }
1827
+ }
1828
+ });
1829
+ reasoningPart.summaryParts[Number(summaryIndex)] = "concluded";
1787
1830
  }
1788
- reasoningPart.summaryParts[event.summary_index] = "active";
1789
1831
  }
1832
+ }
1833
+ if (Predicate.isUndefined(reasoningPart.summaryParts[event.summary_index])) {
1834
+ reasoningPart.summaryParts[event.summary_index] = "active";
1790
1835
  parts.push({
1791
1836
  type: "reasoning-start",
1792
1837
  id: `${event.item_id}:${event.summary_index}`,
@@ -1807,15 +1852,14 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1807
1852
  id: `${event.item_id}:${event.summary_index}`,
1808
1853
  delta: event.delta,
1809
1854
  metadata: {
1810
- openai: {
1811
- ...makeItemIdMetadata(event.item_id)
1812
- }
1855
+ openai: makeItemIdMetadata(event.item_id)
1813
1856
  }
1814
1857
  });
1815
1858
  break;
1816
1859
  }
1817
1860
  case "response.reasoning_summary_part.done":
1818
1861
  {
1862
+ const reasoningPart = getOrCreateReasoningPart(event.item_id);
1819
1863
  // When OpenAI stores message data, we can immediately conclude the
1820
1864
  // reasoning part given that we do not need the encrypted content
1821
1865
  if (config.store === true) {
@@ -1823,17 +1867,15 @@ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* ({
1823
1867
  type: "reasoning-end",
1824
1868
  id: `${event.item_id}:${event.summary_index}`,
1825
1869
  metadata: {
1826
- openai: {
1827
- ...makeItemIdMetadata(event.item_id)
1828
- }
1870
+ openai: makeItemIdMetadata(event.item_id)
1829
1871
  }
1830
1872
  });
1831
1873
  // Mark the summary part concluded
1832
- activeReasoning[event.item_id].summaryParts[event.summary_index] = "concluded";
1874
+ reasoningPart.summaryParts[event.summary_index] = "concluded";
1833
1875
  } else {
1834
1876
  // Mark the summary part as can-conclude given we still need a
1835
1877
  // final summary part with the encrypted content
1836
- activeReasoning[event.item_id].summaryParts[event.summary_index] = "can-conclude";
1878
+ reasoningPart.summaryParts[event.summary_index] = "can-conclude";
1837
1879
  }
1838
1880
  break;
1839
1881
  }
@@ -1936,14 +1978,18 @@ const prepareTools = /*#__PURE__*/Effect.fnUntraced(function* ({
1936
1978
  }
1937
1979
  // Convert the tools in the toolkit to the provider-defined format
1938
1980
  for (const tool of allowedTools) {
1939
- if (Tool.isUserDefined(tool)) {
1981
+ if (Tool.isUserDefined(tool) || Tool.isDynamic(tool)) {
1940
1982
  const strict = Tool.getStrictMode(tool) ?? config.strictJsonSchema ?? true;
1983
+ const description = Tool.getDescription(tool);
1984
+ const parameters = yield* tryToolJsonSchema(tool, "prepareTools");
1941
1985
  tools.push({
1942
1986
  type: "function",
1943
1987
  name: tool.name,
1944
- description: Tool.getDescription(tool) ?? null,
1945
- parameters: Tool.getJsonSchema(tool),
1946
- strict
1988
+ parameters,
1989
+ strict,
1990
+ ...(Predicate.isNotUndefined(description) ? {
1991
+ description
1992
+ } : undefined)
1947
1993
  });
1948
1994
  }
1949
1995
  if (Tool.isProviderDefined(tool)) {
@@ -2111,29 +2157,53 @@ const getEncryptedContent = part => part.options.openai?.encryptedContent ?? nul
2111
2157
  const getImageDetail = part => part.options.openai?.imageDetail ?? "auto";
2112
2158
  const makeItemIdMetadata = itemId => Predicate.isNotUndefined(itemId) ? {
2113
2159
  itemId
2114
- } : undefined;
2160
+ } : {};
2115
2161
  const makeEncryptedContentMetadata = encryptedContent => Predicate.isNotNullish(encryptedContent) ? {
2116
2162
  encryptedContent
2117
2163
  } : undefined;
2118
- const prepareResponseFormat = ({
2164
+ const unsupportedSchemaError = (error, method) => AiError.make({
2165
+ module: "OpenAiLanguageModel",
2166
+ method,
2167
+ reason: new AiError.UnsupportedSchemaError({
2168
+ description: error instanceof Error ? error.message : String(error)
2169
+ })
2170
+ });
2171
+ const tryCodecTransform = (schema, method) => Effect.try({
2172
+ try: () => toCodecOpenAI(schema),
2173
+ catch: error => unsupportedSchemaError(error, method)
2174
+ });
2175
+ const tryJsonSchema = (schema, method) => Effect.try({
2176
+ try: () => Tool.getJsonSchemaFromSchema(schema, {
2177
+ transformer: toCodecOpenAI
2178
+ }),
2179
+ catch: error => unsupportedSchemaError(error, method)
2180
+ });
2181
+ const tryToolJsonSchema = (tool, method) => Effect.try({
2182
+ try: () => Tool.getJsonSchema(tool, {
2183
+ transformer: toCodecOpenAI
2184
+ }),
2185
+ catch: error => unsupportedSchemaError(error, method)
2186
+ });
2187
+ const prepareResponseFormat = /*#__PURE__*/Effect.fnUntraced(function* ({
2119
2188
  config,
2120
2189
  options
2121
- }) => {
2190
+ }) {
2122
2191
  if (options.responseFormat.type === "json") {
2123
2192
  const name = options.responseFormat.objectName;
2124
2193
  const schema = options.responseFormat.schema;
2194
+ const jsonSchema = yield* tryJsonSchema(schema, "prepareResponseFormat");
2125
2195
  return {
2126
2196
  type: "json_schema",
2127
2197
  name,
2128
2198
  description: AST.resolveDescription(schema.ast) ?? "Response with a JSON object",
2129
- schema: Tool.getJsonSchemaFromSchema(schema),
2199
+ schema: jsonSchema,
2130
2200
  strict: config.strictJsonSchema ?? true
2131
2201
  };
2132
2202
  }
2133
2203
  return {
2134
2204
  type: "text"
2135
2205
  };
2136
- };
2206
+ });
2137
2207
  const getModelCapabilities = modelId => {
2138
2208
  const supportsFlexProcessing = modelId.startsWith("o3") || modelId.startsWith("o4-mini") || modelId.startsWith("gpt-5") && !modelId.startsWith("gpt-5-chat");
2139
2209
  const supportsPriorityProcessing = modelId.startsWith("gpt-4") || modelId.startsWith("gpt-5-mini") || modelId.startsWith("gpt-5") && !modelId.startsWith("gpt-5-nano") && !modelId.startsWith("gpt-5-chat") || modelId.startsWith("o3") || modelId.startsWith("o4-mini");
@@ -2170,6 +2240,35 @@ const getApprovalRequestIdMapping = prompt => {
2170
2240
  }
2171
2241
  return mapping;
2172
2242
  };
2243
+ const normalizeMcpToolCall = /*#__PURE__*/Effect.fnUntraced(function* ({
2244
+ toolNameMapper,
2245
+ toolParams,
2246
+ method
2247
+ }) {
2248
+ const toolName = toolNameMapper.getCustomName("mcp");
2249
+ if (typeof toolParams !== "string") {
2250
+ return {
2251
+ toolName,
2252
+ params: toolParams
2253
+ };
2254
+ }
2255
+ const params = yield* Effect.try({
2256
+ try: () => Tool.unsafeSecureJsonParse(toolParams),
2257
+ catch: cause => AiError.make({
2258
+ module: "OpenAiLanguageModel",
2259
+ method,
2260
+ reason: new AiError.ToolParameterValidationError({
2261
+ toolName,
2262
+ toolParams,
2263
+ description: `Failed to securely JSON parse tool parameters: ${cause}`
2264
+ })
2265
+ })
2266
+ });
2267
+ return {
2268
+ toolName,
2269
+ params
2270
+ };
2271
+ });
2173
2272
  const getUsage = usage => {
2174
2273
  if (Predicate.isNullish(usage)) {
2175
2274
  return {
@@ -2188,8 +2287,8 @@ const getUsage = usage => {
2188
2287
  }
2189
2288
  const inputTokens = usage.input_tokens;
2190
2289
  const outputTokens = usage.output_tokens;
2191
- const cachedTokens = usage.input_tokens_details.cached_tokens;
2192
- const reasoningTokens = usage.output_tokens_details.reasoning_tokens;
2290
+ const cachedTokens = getUsageTokenDetail(usage.input_tokens_details, "cached_tokens");
2291
+ const reasoningTokens = getUsageTokenDetail(usage.output_tokens_details, "reasoning_tokens");
2193
2292
  return {
2194
2293
  inputTokens: {
2195
2294
  uncached: inputTokens - cachedTokens,
@@ -2204,4 +2303,49 @@ const getUsage = usage => {
2204
2303
  }
2205
2304
  };
2206
2305
  };
2306
+ const toServiceTier = value => {
2307
+ switch (value) {
2308
+ case "default":
2309
+ case "auto":
2310
+ case "flex":
2311
+ case "scale":
2312
+ case "priority":
2313
+ return {
2314
+ metadata: {
2315
+ openai: {
2316
+ serviceTier: value
2317
+ }
2318
+ }
2319
+ };
2320
+ default:
2321
+ return undefined;
2322
+ }
2323
+ };
2324
+ const getUsageTokenDetail = (details, key) => Predicate.hasProperty(details, key) && typeof details[key] === "number" ? details[key] : 0;
2325
+ const transformToolCallParams = /*#__PURE__*/Effect.fnUntraced(function* (tools, toolName, toolParams) {
2326
+ const tool = tools.find(tool => tool.name === toolName);
2327
+ if (Predicate.isUndefined(tool)) {
2328
+ return yield* AiError.make({
2329
+ module: "OpenAiLanguageModel",
2330
+ method: "makeResponse",
2331
+ reason: new AiError.ToolNotFoundError({
2332
+ toolName,
2333
+ availableTools: tools.map(tool => tool.name)
2334
+ })
2335
+ });
2336
+ }
2337
+ const {
2338
+ codec
2339
+ } = yield* tryCodecTransform(tool.parametersSchema, "makeResponse");
2340
+ const transform = Schema.decodeEffect(codec);
2341
+ return yield* transform(toolParams).pipe(Effect.mapError(error => AiError.make({
2342
+ module: "OpenAiLanguageModel",
2343
+ method: "makeResponse",
2344
+ reason: new AiError.ToolParameterValidationError({
2345
+ toolName,
2346
+ toolParams,
2347
+ description: error.issue.toString()
2348
+ })
2349
+ })));
2350
+ });
2207
2351
  //# sourceMappingURL=OpenAiLanguageModel.js.map