@core-ai/openai 0.23.0 → 0.25.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.
@@ -2,8 +2,10 @@
2
2
  import {
3
3
  getRegisteredModelCapabilities,
4
4
  MULTIMODAL_INPUT_MODALITIES,
5
+ SUPPORTED_TOOL_SCHEMA_STRICTNESS,
5
6
  stripModelDateSuffix,
6
7
  TEXT_ONLY_MODALITIES,
8
+ UNSUPPORTED_TOOL_SCHEMA_STRICTNESS,
7
9
  UNKNOWN_MODEL
8
10
  } from "@core-ai/core-ai";
9
11
  var STANDARD_EFFORTS = [
@@ -37,7 +39,8 @@ function createCapabilities({
37
39
  supportedEfforts,
38
40
  restrictsSamplingParams,
39
41
  maxTokensParameter = "max_completion_tokens",
40
- modalities = MULTIMODAL_INPUT_MODALITIES
42
+ modalities = MULTIMODAL_INPUT_MODALITIES,
43
+ strictToolSchemas = SUPPORTED_TOOL_SCHEMA_STRICTNESS
41
44
  }) {
42
45
  return {
43
46
  reasoning: {
@@ -47,6 +50,9 @@ function createCapabilities({
47
50
  supportedToolChoices: ["auto", "none", "required", "tool"]
48
51
  },
49
52
  modalities,
53
+ tools: {
54
+ strictSchemas: strictToolSchemas
55
+ },
50
56
  chatCompletions: {
51
57
  maxTokensParameter
52
58
  }
@@ -83,7 +89,8 @@ var GPT_5_HIGH_REASONING_CAPABILITIES = createCapabilities({
83
89
  });
84
90
  function createNoReasoningCapabilities({
85
91
  maxTokensParameter,
86
- modalities = MULTIMODAL_INPUT_MODALITIES
92
+ modalities = MULTIMODAL_INPUT_MODALITIES,
93
+ strictToolSchemas = SUPPORTED_TOOL_SCHEMA_STRICTNESS
87
94
  }) {
88
95
  return {
89
96
  reasoning: {
@@ -93,6 +100,9 @@ function createNoReasoningCapabilities({
93
100
  supportedToolChoices: ["auto", "none", "required", "tool"]
94
101
  },
95
102
  modalities,
103
+ tools: {
104
+ strictSchemas: strictToolSchemas
105
+ },
96
106
  chatCompletions: {
97
107
  maxTokensParameter
98
108
  }
@@ -101,21 +111,19 @@ function createNoReasoningCapabilities({
101
111
  var NO_REASONING_CAPABILITIES = createNoReasoningCapabilities({
102
112
  maxTokensParameter: "max_tokens"
103
113
  });
104
- var NO_REASONING_TEXT_ONLY_CAPABILITIES = createNoReasoningCapabilities({
105
- maxTokensParameter: "max_tokens",
106
- modalities: TEXT_ONLY_MODALITIES
107
- });
108
114
  var NO_REASONING_EFFORT_TEXT_ONLY_CAPABILITIES = createNoReasoningCapabilities({
109
115
  maxTokensParameter: "max_completion_tokens",
110
116
  modalities: TEXT_ONLY_MODALITIES
111
117
  });
112
118
  var AUDIO_CAPABILITIES = createNoReasoningCapabilities({
113
119
  maxTokensParameter: "max_completion_tokens",
114
- modalities: OPENAI_AUDIO_INPUT_MODALITIES
120
+ modalities: OPENAI_AUDIO_INPUT_MODALITIES,
121
+ strictToolSchemas: UNSUPPORTED_TOOL_SCHEMA_STRICTNESS
115
122
  });
116
123
  var GPT_4O_AUDIO_CAPABILITIES = createNoReasoningCapabilities({
117
124
  maxTokensParameter: "max_tokens",
118
- modalities: OPENAI_AUDIO_INPUT_MODALITIES
125
+ modalities: OPENAI_AUDIO_INPUT_MODALITIES,
126
+ strictToolSchemas: UNSUPPORTED_TOOL_SCHEMA_STRICTNESS
119
127
  });
120
128
  var O_SERIES_MAX_REASONING_CAPABILITIES = createCapabilities({
121
129
  supportedEfforts: MAX_EFFORTS,
@@ -126,7 +134,17 @@ var O_SERIES_TEXT_ONLY_CAPABILITIES = createCapabilities({
126
134
  restrictsSamplingParams: false,
127
135
  modalities: TEXT_ONLY_MODALITIES
128
136
  });
137
+ var LEGACY_NO_STRICT_TOOLS_CAPABILITIES = createNoReasoningCapabilities({
138
+ maxTokensParameter: "max_tokens",
139
+ strictToolSchemas: UNSUPPORTED_TOOL_SCHEMA_STRICTNESS
140
+ });
141
+ var LEGACY_NO_STRICT_TOOLS_TEXT_ONLY_CAPABILITIES = createNoReasoningCapabilities({
142
+ maxTokensParameter: "max_tokens",
143
+ modalities: TEXT_ONLY_MODALITIES,
144
+ strictToolSchemas: UNSUPPORTED_TOOL_SCHEMA_STRICTNESS
145
+ });
129
146
  var OPENAI_MODEL_CAPABILITIES = {
147
+ "gpt-4o-2024-05-13": LEGACY_NO_STRICT_TOOLS_CAPABILITIES,
130
148
  "gpt-5.6-sol": GPT_5_MAX_REASONING_CAPABILITIES,
131
149
  "gpt-5.6-terra": SAMPLING_RESTRICTED_STANDARD_CAPABILITIES,
132
150
  "gpt-5.6-luna": GPT_5_MINIMAL_REASONING_CAPABILITIES,
@@ -160,8 +178,8 @@ var OPENAI_MODEL_CAPABILITIES = {
160
178
  "gpt-4.1-nano": NO_REASONING_CAPABILITIES,
161
179
  "gpt-4o": NO_REASONING_CAPABILITIES,
162
180
  "gpt-4o-mini": NO_REASONING_CAPABILITIES,
163
- "gpt-4-turbo": NO_REASONING_CAPABILITIES,
164
- "gpt-3.5-turbo": NO_REASONING_TEXT_ONLY_CAPABILITIES,
181
+ "gpt-4-turbo": LEGACY_NO_STRICT_TOOLS_CAPABILITIES,
182
+ "gpt-3.5-turbo": LEGACY_NO_STRICT_TOOLS_TEXT_ONLY_CAPABILITIES,
165
183
  "gpt-audio-1.5": AUDIO_CAPABILITIES,
166
184
  "gpt-audio": AUDIO_CAPABILITIES,
167
185
  "gpt-audio-mini": AUDIO_CAPABILITIES,
@@ -177,7 +195,14 @@ var OPENAI_REASONING_EFFORT_MAP = {
177
195
  max: "xhigh"
178
196
  };
179
197
  function getOpenAIModelCapabilities(modelId) {
180
- return getRegisteredModelCapabilities(OPENAI_MODEL_CAPABILITIES, modelId) ?? UNKNOWN_MODEL_CAPABILITIES;
198
+ return getRegisteredModelCapabilities(
199
+ OPENAI_MODEL_CAPABILITIES,
200
+ getFineTuneBaseModelId(modelId) ?? modelId
201
+ ) ?? UNKNOWN_MODEL_CAPABILITIES;
202
+ }
203
+ var FINE_TUNE_MODEL_ID_PATTERN = /^ft:([^:]+):/;
204
+ function getFineTuneBaseModelId(modelId) {
205
+ return FINE_TUNE_MODEL_ID_PATTERN.exec(modelId)?.[1];
181
206
  }
182
207
  function toOpenAIResponsesCapabilities(capabilities) {
183
208
  if (!capabilities.modalities.input.includes("audio")) {
@@ -285,17 +310,37 @@ import {
285
310
  } from "@core-ai/core-ai";
286
311
 
287
312
  // src/shared/tools.ts
288
- import { zodSchemaToJsonSchema } from "@core-ai/core-ai";
289
- function convertTools(tools) {
313
+ import {
314
+ normalizeStrictJsonSchema,
315
+ validateToolSchemaStrictness,
316
+ zodSchemaToJsonSchema
317
+ } from "@core-ai/core-ai";
318
+ function convertTools(tools, options) {
319
+ validateToolSchemaStrictness({ tools, ...options });
290
320
  return Object.values(tools).map((tool) => ({
291
321
  type: "function",
292
322
  function: {
293
323
  name: tool.name,
294
324
  description: tool.description,
295
- parameters: zodSchemaToJsonSchema(tool.parameters)
325
+ parameters: convertToolParameters(tool),
326
+ ...tool.strict === true ? { strict: true } : {}
296
327
  }
297
328
  }));
298
329
  }
330
+ function convertResponseTools(tools, options) {
331
+ validateToolSchemaStrictness({ tools, ...options });
332
+ return Object.values(tools).map((tool) => ({
333
+ type: "function",
334
+ name: tool.name,
335
+ description: tool.description,
336
+ parameters: convertToolParameters(tool),
337
+ strict: tool.strict === true
338
+ }));
339
+ }
340
+ function convertToolParameters(tool) {
341
+ const schema = zodSchemaToJsonSchema(tool.parameters);
342
+ return tool.strict === true ? normalizeStrictJsonSchema(schema) : schema;
343
+ }
299
344
  function convertToolChoice(choice) {
300
345
  if (typeof choice === "string") {
301
346
  return choice;
@@ -529,7 +574,13 @@ function createRequestBase(modelId, options, adapterOptions) {
529
574
  return {
530
575
  model: modelId,
531
576
  messages: convertMessages(options.messages, adapterOptions),
532
- ...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertTools(options.tools) } : {},
577
+ ...options.tools && Object.keys(options.tools).length > 0 ? {
578
+ tools: convertTools(options.tools, {
579
+ capabilities: adapterOptions.capabilities,
580
+ modelId,
581
+ providerId: adapterOptions.providerId
582
+ })
583
+ } : {},
533
584
  ...options.toolChoice ? { tool_choice: convertToolChoice(options.toolChoice) } : {},
534
585
  ...reasoningFields,
535
586
  ...mapSamplingToRequestFields(modelId, options, adapterOptions)
@@ -878,6 +929,7 @@ import {
878
929
  ContextLengthExceededError,
879
930
  ModelOverloadedError,
880
931
  ProviderError,
932
+ ProviderQuotaExceededError,
881
933
  RateLimitError,
882
934
  ServiceUnavailableError,
883
935
  asRecord,
@@ -921,8 +973,8 @@ function wrapOpenAIError(error, provider = "openai") {
921
973
  if (isAzureOpenAIBackendCapacityError(error)) {
922
974
  return new ServiceUnavailableError(message, provider, options);
923
975
  }
924
- if (isOpenAIInsufficientQuota(error)) {
925
- return new ProviderError(message, provider, options);
976
+ if (isOpenAIQuotaExceeded(error, statusCode)) {
977
+ return new ProviderQuotaExceededError(message, provider, options);
926
978
  }
927
979
  if (isAzureOpenAINoCapacity(error)) {
928
980
  return new ModelOverloadedError(message, provider, options);
@@ -1004,13 +1056,14 @@ function isAzureOpenAIBackendCapacityError(error) {
1004
1056
  const providerMessage = getProviderMessage(error);
1005
1057
  return type === "invalid_request_error" && providerMessage?.toLowerCase() === "backend error.";
1006
1058
  }
1007
- function isOpenAIInsufficientQuota(error) {
1059
+ function isOpenAIQuotaExceeded(error, statusCode) {
1008
1060
  const code = getProviderErrorCode(error);
1009
1061
  const type = getProviderErrorType(error);
1010
- return code === "insufficient_quota" || type === "insufficient_quota" || code === "credit_balance_exhausted";
1062
+ return statusCode === 402 || code === "insufficient_quota" || type === "insufficient_quota" || code === "credit_balance_exhausted";
1011
1063
  }
1012
1064
  function isAzureOpenAINoCapacity(error) {
1013
- return getProviderErrorCode(error) === "NoCapacity";
1065
+ const code = getProviderErrorCode(error);
1066
+ return code === "NoCapacity" || code === "no_capacity";
1014
1067
  }
1015
1068
  function getProviderMessage(error) {
1016
1069
  if (error instanceof APIError) {
@@ -1396,12 +1449,10 @@ import {
1396
1449
  } from "@core-ai/core-ai";
1397
1450
 
1398
1451
  // src/chat-model.ts
1399
- import {
1400
- createObjectStream as createObjectStream2,
1401
- createChatStream as createChatStream2
1402
- } from "@core-ai/core-ai";
1452
+ import { createObjectStream as createObjectStream2, createChatStream as createChatStream2 } from "@core-ai/core-ai";
1403
1453
 
1404
1454
  // src/chat-adapter.ts
1455
+ import { APIError as APIError2 } from "openai";
1405
1456
  import {
1406
1457
  getProviderMetadata as getProviderMetadata2,
1407
1458
  clampReasoningEffort as clampReasoningEffort2,
@@ -1411,9 +1462,11 @@ import {
1411
1462
  } from "@core-ai/core-ai";
1412
1463
  var DEFAULT_PROVIDER_ID = "openai";
1413
1464
  function resolveAdapterOptions(modelId, adapterOptions) {
1414
- const capabilities = adapterOptions.capabilities ?? getOpenAIModelCapabilities(modelId);
1415
1465
  return {
1416
- capabilities: toOpenAIResponsesCapabilities(capabilities),
1466
+ // Callers that pass capabilities (the provider factory) have already
1467
+ // applied the Responses-specific restrictions; only the fallback
1468
+ // lookup applies them here.
1469
+ capabilities: adapterOptions.capabilities ?? toOpenAIResponsesCapabilities(getOpenAIModelCapabilities(modelId)),
1417
1470
  // Also the ownership key for encrypted reasoning metadata — Azure
1418
1471
  // (`azure-openai`) must not share the `openai` namespace.
1419
1472
  providerId: adapterOptions.providerId ?? DEFAULT_PROVIDER_ID
@@ -1603,7 +1656,13 @@ function createRequestBase2(modelId, options, { capabilities, providerId }) {
1603
1656
  includeReasoning: capabilities.reasoning.mode !== "unsupported",
1604
1657
  providerId
1605
1658
  }),
1606
- ...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertResponseTools(options.tools) } : {},
1659
+ ...options.tools && Object.keys(options.tools).length > 0 ? {
1660
+ tools: convertResponseTools(options.tools, {
1661
+ capabilities,
1662
+ modelId,
1663
+ providerId
1664
+ })
1665
+ } : {},
1607
1666
  ...options.toolChoice ? { tool_choice: convertResponseToolChoice(options.toolChoice) } : {},
1608
1667
  ...mapReasoningToRequestFields2(options, capabilities),
1609
1668
  ...mapSamplingToRequestFields2(options)
@@ -1619,14 +1678,6 @@ function validateResponsesInputModalities(options) {
1619
1678
  throw error;
1620
1679
  }
1621
1680
  }
1622
- function convertResponseTools(tools) {
1623
- return convertTools(tools).map((tool) => ({
1624
- type: "function",
1625
- name: tool.function.name,
1626
- description: tool.function.description,
1627
- parameters: tool.function.parameters
1628
- }));
1629
- }
1630
1681
  function convertResponseToolChoice(choice) {
1631
1682
  const converted = convertToolChoice(choice);
1632
1683
  if (typeof converted === "string") {
@@ -1663,6 +1714,13 @@ function mapOpenAIProviderOptionsToRequestFields2(options) {
1663
1714
  };
1664
1715
  }
1665
1716
  function mapGenerateResponse2(response, adapterOptions = {}) {
1717
+ if (response.status === "failed") {
1718
+ throw createInBandStreamError(
1719
+ response.error ?? {
1720
+ message: "Response failed without error details"
1721
+ }
1722
+ );
1723
+ }
1666
1724
  const providerId = adapterOptions.providerId ?? DEFAULT_PROVIDER_ID;
1667
1725
  const parts = [];
1668
1726
  for (const item of response.output) {
@@ -1864,6 +1922,16 @@ async function* transformStream2(stream, adapterOptions = {}) {
1864
1922
  };
1865
1923
  };
1866
1924
  for await (const event of stream) {
1925
+ if (event.type === "error") {
1926
+ throw createInBandStreamError(event);
1927
+ }
1928
+ if (event.type === "response.failed") {
1929
+ throw createInBandStreamError(
1930
+ event.response.error ?? {
1931
+ message: "Response failed without error details"
1932
+ }
1933
+ );
1934
+ }
1867
1935
  if (event.type === "response.reasoning_summary_text.delta") {
1868
1936
  const summaryPart = {
1869
1937
  itemId: event.item_id,
@@ -2003,7 +2071,10 @@ async function* transformStream2(stream, adapterOptions = {}) {
2003
2071
  }
2004
2072
  }
2005
2073
  const reasoningEndEvent2 = getNextReasoningEndEvent(
2006
- createReasoningProviderMetadata(providerId, encryptedContent)
2074
+ createReasoningProviderMetadata(
2075
+ providerId,
2076
+ encryptedContent
2077
+ )
2007
2078
  );
2008
2079
  if (reasoningEndEvent2) {
2009
2080
  yield reasoningEndEvent2;
@@ -2040,7 +2111,7 @@ async function* transformStream2(stream, adapterOptions = {}) {
2040
2111
  }
2041
2112
  continue;
2042
2113
  }
2043
- if (event.type === "response.completed") {
2114
+ if (event.type === "response.completed" || event.type === "response.incomplete") {
2044
2115
  latestResponse = event.response;
2045
2116
  yield* closeText();
2046
2117
  const reasoningEndEvent2 = getNextReasoningEndEvent(
@@ -2088,6 +2159,9 @@ async function* transformStream2(stream, adapterOptions = {}) {
2088
2159
  usage
2089
2160
  };
2090
2161
  }
2162
+ function createInBandStreamError(body) {
2163
+ return new APIError2(void 0, body, body.message, void 0);
2164
+ }
2091
2165
  function mapReasoningToRequestFields2(options, capabilities) {
2092
2166
  if (!options.reasoning) {
2093
2167
  return {};
@@ -2137,7 +2211,11 @@ function createOpenAIChatModel(client, modelId, capabilities, providerId = DEFAU
2137
2211
  request,
2138
2212
  options.signal
2139
2213
  );
2140
- return mapGenerateResponse2(response, { providerId: provider });
2214
+ try {
2215
+ return mapGenerateResponse2(response, { providerId: provider });
2216
+ } catch (error) {
2217
+ throw wrapOpenAIError(error, provider);
2218
+ }
2141
2219
  }
2142
2220
  async function streamChat(options) {
2143
2221
  const request = createStreamRequest2(modelId, options, adapterOptions);
@@ -2286,23 +2364,18 @@ function createOpenAIProvider(options, factoryOptions = {}) {
2286
2364
  });
2287
2365
  const providerId = factoryOptions.providerId ?? "openai";
2288
2366
  const compatibilityOptions = typeof factoryOptions.compatibility === "object" ? factoryOptions.compatibility : void 0;
2289
- const createResponsesModel = (modelId) => {
2290
- const capabilities = getRegisteredModelCapabilities2(
2291
- factoryOptions.modelCapabilities,
2292
- modelId
2293
- ) ?? getOpenAIModelCapabilities(modelId);
2294
- return createOpenAIChatModel(
2295
- client,
2296
- modelId,
2297
- toOpenAIResponsesCapabilities(capabilities),
2298
- providerId
2299
- );
2300
- };
2367
+ const resolveCapabilities = (modelId) => getRegisteredModelCapabilities2(
2368
+ factoryOptions.modelCapabilities,
2369
+ modelId
2370
+ ) ?? getOpenAIModelCapabilities(modelId);
2371
+ const createResponsesModel = (modelId) => createOpenAIChatModel(
2372
+ client,
2373
+ modelId,
2374
+ toOpenAIResponsesCapabilities(resolveCapabilities(modelId)),
2375
+ providerId
2376
+ );
2301
2377
  const createChatCompletionsModel = (modelId) => {
2302
- const capabilities = getRegisteredModelCapabilities2(
2303
- factoryOptions.modelCapabilities,
2304
- modelId
2305
- ) ?? getOpenAIModelCapabilities(modelId);
2378
+ const capabilities = resolveCapabilities(modelId);
2306
2379
  const compatibility = factoryOptions.compatibility ? {
2307
2380
  reasoning: typeof compatibilityOptions?.reasoning === "object" ? {
2308
2381
  ...compatibilityOptions.reasoning,
package/dist/compat.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  getOpenAIModelCapabilities,
5
5
  openaiCompatGenerateProviderOptionsSchema,
6
6
  openaiCompatProviderOptionsSchema
7
- } from "./chunk-KLH5XIHP.js";
7
+ } from "./chunk-IZPTRJP3.js";
8
8
 
9
9
  // src/compat/provider.ts
10
10
  import OpenAI from "openai";
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  openaiImageProviderOptionsSchema,
11
11
  openaiResponsesGenerateProviderOptionsSchema,
12
12
  openaiResponsesProviderOptionsSchema
13
- } from "./chunk-KLH5XIHP.js";
13
+ } from "./chunk-IZPTRJP3.js";
14
14
 
15
15
  // src/provider.ts
16
16
  function createOpenAI(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@core-ai/openai",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "OpenAI provider package for @core-ai/core-ai",
5
5
  "license": "MIT",
6
6
  "author": "Omnifact (https://omnifact.ai)",
@@ -50,7 +50,7 @@
50
50
  "test:watch": "vitest"
51
51
  },
52
52
  "dependencies": {
53
- "@core-ai/core-ai": "^0.23.0",
53
+ "@core-ai/core-ai": "^0.25.0",
54
54
  "openai": "^6.46.0"
55
55
  },
56
56
  "peerDependencies": {