@core-ai/core-ai 0.18.0 → 0.20.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.
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ type UserMessage = {
9
9
  role: 'user';
10
10
  content: string | UserContentPart[];
11
11
  };
12
- type UserContentPart = TextPart | ImagePart | FilePart;
12
+ type UserContentPart = TextPart | ImagePart | FilePart | AudioPart;
13
13
  type TextPart = {
14
14
  type: 'text';
15
15
  text: string;
@@ -36,6 +36,14 @@ type FilePart = {
36
36
  mimeType: string;
37
37
  filename?: string;
38
38
  };
39
+ type AudioPart = {
40
+ type: 'audio';
41
+ source: {
42
+ type: 'base64';
43
+ mediaType: string;
44
+ data: string;
45
+ };
46
+ };
39
47
  type ReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' | 'max';
40
48
  type ReasoningConfig = {
41
49
  effort: ReasoningEffort;
@@ -59,20 +67,35 @@ type ReasoningPart = {
59
67
  metadata?: Record<string, unknown>;
60
68
  /**
61
69
  * Provider-namespaced metadata for this reasoning block. The top-level key is
62
- * the provider identifier (e.g. `'anthropic'`, `'openai'`), which also serves as
63
- * the ownership discriminator: an adapter checks for the presence of its own key
64
- * to detect cross-provider blocks. Cross-provider blocks are downgraded to plain
65
- * text (preserving context) rather than forwarding opaque metadata that would
66
- * cause an API error on the receiving provider.
70
+ * the provider identifier (e.g. `'anthropic'`, `'openai'`, `'azure-openai'`),
71
+ * which also serves as the ownership discriminator: an adapter checks for the
72
+ * presence of its own key to detect cross-provider blocks. Cross-provider
73
+ * blocks are downgraded to plain text (preserving context) rather than
74
+ * forwarding opaque metadata that would cause an API error on the receiving
75
+ * provider.
67
76
  *
68
- * @example Anthropic: `{ anthropic: { signature: '...' } }`
69
- * @example OpenAI: `{ openai: { encryptedContent: '...' } }`
77
+ * @example Anthropic: `{ anthropic: { signature: '...' } }`
78
+ * @example OpenAI: `{ openai: { encryptedContent: '...' } }`
79
+ * @example Azure OpenAI: `{ 'azure-openai': { encryptedContent: '...' } }`
70
80
  */
71
81
  providerMetadata?: Record<string, Record<string, unknown>>;
72
82
  };
73
83
  type ToolCallPart = {
74
84
  type: 'tool-call';
75
85
  toolCall: ToolCall;
86
+ /**
87
+ * Provider-namespaced metadata for this tool call. The top-level key is the
88
+ * provider identifier and serves as the ownership discriminator, exactly as
89
+ * for `ReasoningPart`: an adapter only forwards metadata stored under its
90
+ * own key and ignores blocks produced by another provider.
91
+ *
92
+ * Some providers require this data to be replayed verbatim. Gemini 3 rejects
93
+ * a request when a function call from the current turn is sent back without
94
+ * the thought signature it was issued with.
95
+ *
96
+ * @example Google: `{ google: { thoughtSignature: '...' } }`
97
+ */
98
+ providerMetadata?: Record<string, Record<string, unknown>>;
76
99
  };
77
100
  type AssistantContentPart = AssistantTextPart | ReasoningPart | ToolCallPart;
78
101
  type AssistantMessage = {
@@ -111,6 +134,21 @@ type ToolChoice = 'auto' | 'none' | 'required' | {
111
134
  toolName: string;
112
135
  };
113
136
  type ToolChoiceMode = 'auto' | 'none' | 'required' | 'tool';
137
+ /**
138
+ * Modalities a chat model can accept in user messages.
139
+ *
140
+ * `file` covers document attachments such as PDFs. `video` is reserved for a
141
+ * future input part.
142
+ */
143
+ type ChatInputModality = 'text' | 'image' | 'file' | 'audio' | 'video';
144
+ /**
145
+ * Modalities a chat model can emit as assistant content.
146
+ *
147
+ * Dedicated generators (`ImageModel`, and future audio/video models) are
148
+ * separate operations. Chat `output` describes native multimodal responses
149
+ * from `generate` / `stream`, not those dedicated APIs.
150
+ */
151
+ type ChatOutputModality = 'text' | 'image' | 'audio' | 'video';
114
152
  type ModelCapabilities = {
115
153
  reasoning: {
116
154
  mode: 'unsupported' | 'optional' | 'always-on';
@@ -122,6 +160,15 @@ type ModelCapabilities = {
122
160
  restrictsSamplingParams: boolean;
123
161
  supportedToolChoices: readonly ToolChoiceMode[];
124
162
  };
163
+ modalities: {
164
+ /** Modalities accepted in user messages. Always includes `'text'`. */
165
+ input: readonly ChatInputModality[];
166
+ /**
167
+ * Modalities the model can emit as assistant content. Always includes
168
+ * `'text'`. Does not describe dedicated `ImageModel` generation.
169
+ */
170
+ output: readonly ChatOutputModality[];
171
+ };
125
172
  };
126
173
  type ChatModel = {
127
174
  readonly provider: string;
@@ -278,6 +325,7 @@ type StreamEvent = {
278
325
  } | {
279
326
  type: 'tool-call-end';
280
327
  toolCall: ToolCall;
328
+ providerMetadata?: Record<string, Record<string, unknown>>;
281
329
  } | {
282
330
  type: 'finish';
283
331
  finishReason: FinishReason;
@@ -373,6 +421,24 @@ declare class CoreAIError extends Error {
373
421
  declare class ValidationError extends CoreAIError {
374
422
  constructor(message: string, cause?: unknown, provider?: string);
375
423
  }
424
+ type UnsupportedInputModalityErrorOptions = {
425
+ modelId: string;
426
+ providerId: string;
427
+ requestedModalities: readonly string[];
428
+ supportedModalities: readonly string[];
429
+ unsupportedModalities: readonly string[];
430
+ };
431
+ /**
432
+ * Thrown when user messages include content parts the model does not accept.
433
+ * Extends {@link ValidationError} so existing `instanceof ValidationError`
434
+ * checks still match.
435
+ */
436
+ declare class UnsupportedInputModalityError extends ValidationError {
437
+ readonly requestedModalities: readonly string[];
438
+ readonly supportedModalities: readonly string[];
439
+ readonly unsupportedModalities: readonly string[];
440
+ constructor(options: UnsupportedInputModalityErrorOptions);
441
+ }
376
442
  declare class AbortedError extends CoreAIError {
377
443
  constructor(cause?: unknown, provider?: string);
378
444
  }
@@ -482,6 +548,36 @@ declare function zodSchemaToJsonSchema(schema: z.ZodType): Record<string, unknow
482
548
  declare function stripModelDateSuffix(modelId: string): string;
483
549
 
484
550
  declare function clampReasoningEffort(effort: ReasoningEffort, supportedEfforts: readonly ReasoningEffort[]): ReasoningEffort;
551
+ /** Text in, text out — the default chat modality profile. */
552
+ declare const TEXT_ONLY_MODALITIES: {
553
+ readonly input: readonly ["text"];
554
+ readonly output: readonly ["text"];
555
+ };
556
+ /**
557
+ * Text, image, and file in; text out.
558
+ *
559
+ * Typical vision / document chat models. Audio support is advertised separately
560
+ * by providers whose adapters accept `AudioPart`.
561
+ */
562
+ declare const MULTIMODAL_INPUT_MODALITIES: {
563
+ readonly input: readonly ["text", "image", "file"];
564
+ readonly output: readonly ["text"];
565
+ };
566
+ declare function supportsInputModality(capabilities: ModelCapabilities, modality: ChatInputModality): boolean;
567
+ declare function supportsOutputModality(capabilities: ModelCapabilities, modality: ChatOutputModality): boolean;
568
+
569
+ type ValidateInputModalitiesOptions = {
570
+ messages: Message[];
571
+ capabilities: ModelCapabilities;
572
+ modelId: string;
573
+ providerId: string;
574
+ };
575
+ /**
576
+ * Rejects user content parts whose modalities are not in
577
+ * `capabilities.modalities.input`. Part `type` values map 1:1 to input
578
+ * modalities (`text`, `image`, `file`, and future `audio` / `video`).
579
+ */
580
+ declare function validateInputModalities({ messages, capabilities, modelId, providerId, }: ValidateInputModalitiesOptions): void;
485
581
 
486
582
  declare const UNKNOWN_MODEL: unique symbol;
487
583
  type ModelCapabilitiesRegistry<TCapabilities extends ModelCapabilities = ModelCapabilities> = Record<string, TCapabilities> & {
@@ -552,4 +648,4 @@ type GenerateImageParams = ImageGenerateOptions & {
552
648
  };
553
649
  declare function generateImage(params: GenerateImageParams): Promise<ImageGenerateResult>;
554
650
 
555
- export { AbortedError, type AssistantContentPart, type AssistantMessage, type AssistantTextPart, type BaseGenerateOptions, type ChatInputTokenDetails, type ChatModel, type ChatModelMiddleware, type ChatOutputTokenDetails, type ChatStream, type ChatUsage, ContextLengthExceededError, type ContextLengthExceededErrorOptions, CoreAIError, type EmbedOptions, type EmbedProviderOptions, type EmbedResult, type EmbeddingModel, type EmbeddingModelMiddleware, type EmbeddingUsage, type FilePart, type FinishReason, type GenerateObjectOptions, type GenerateObjectResult, type GenerateOptions, type GenerateProviderOptions, type GenerateResult, type GeneratedImage, type ImageGenerateOptions, type ImageGenerateResult, type ImageModel, type ImageModelMiddleware, type ImagePart, type ImageProviderOptions, type Message, type ModelCapabilities, type ModelCapabilitiesRegistry, ModelOverloadedError, type ModelOverloadedErrorOptions, type ObjectStream, type ObjectStreamEvent, ProviderError, type ProviderErrorOptions, RateLimitError, type RateLimitErrorOptions, type ReasoningConfig, type ReasoningEffort, type ReasoningPart, RetryableProviderError, ServiceUnavailableError, type ServiceUnavailableErrorOptions, StreamAbortedError, type StreamEvent, type StreamObjectOptions, StructuredOutputError, StructuredOutputNoObjectGeneratedError, StructuredOutputParseError, StructuredOutputValidationError, type SystemMessage, type TextPart, type ToolCall, type ToolCallPart, type ToolChoice, type ToolDefinition, type ToolResultMessage, type ToolSet, UNKNOWN_MODEL, type UserContentPart, type UserMessage, ValidationError, asObject, asRecord, assistantMessage, clampReasoningEffort, createChatStream, createObjectStream, defineTool, embed, generate, generateImage, generateObject, getErrorMessage, getHttpStatusCode, getProviderMetadata, getRegisteredModelCapabilities, getRetryAfterSecondsFromError, getString, isAbortErrorByName, isRateLimitStatus, isTransientUnavailableStatus, parseRetryAfterSeconds, resultToMessage, safeParseJsonObject, stream, streamObject, stripModelDateSuffix, wrapChatModel, wrapEmbeddingModel, wrapImageModel, zodSchemaToJsonSchema };
651
+ export { AbortedError, type AssistantContentPart, type AssistantMessage, type AssistantTextPart, type AudioPart, type BaseGenerateOptions, type ChatInputModality, type ChatInputTokenDetails, type ChatModel, type ChatModelMiddleware, type ChatOutputModality, type ChatOutputTokenDetails, type ChatStream, type ChatUsage, ContextLengthExceededError, type ContextLengthExceededErrorOptions, CoreAIError, type EmbedOptions, type EmbedProviderOptions, type EmbedResult, type EmbeddingModel, type EmbeddingModelMiddleware, type EmbeddingUsage, type FilePart, type FinishReason, type GenerateObjectOptions, type GenerateObjectResult, type GenerateOptions, type GenerateProviderOptions, type GenerateResult, type GeneratedImage, type ImageGenerateOptions, type ImageGenerateResult, type ImageModel, type ImageModelMiddleware, type ImagePart, type ImageProviderOptions, MULTIMODAL_INPUT_MODALITIES, type Message, type ModelCapabilities, type ModelCapabilitiesRegistry, ModelOverloadedError, type ModelOverloadedErrorOptions, type ObjectStream, type ObjectStreamEvent, ProviderError, type ProviderErrorOptions, RateLimitError, type RateLimitErrorOptions, type ReasoningConfig, type ReasoningEffort, type ReasoningPart, RetryableProviderError, ServiceUnavailableError, type ServiceUnavailableErrorOptions, StreamAbortedError, type StreamEvent, type StreamObjectOptions, StructuredOutputError, StructuredOutputNoObjectGeneratedError, StructuredOutputParseError, StructuredOutputValidationError, type SystemMessage, TEXT_ONLY_MODALITIES, type TextPart, type ToolCall, type ToolCallPart, type ToolChoice, type ToolDefinition, type ToolResultMessage, type ToolSet, UNKNOWN_MODEL, UnsupportedInputModalityError, type UnsupportedInputModalityErrorOptions, type UserContentPart, type UserMessage, type ValidateInputModalitiesOptions, ValidationError, asObject, asRecord, assistantMessage, clampReasoningEffort, createChatStream, createObjectStream, defineTool, embed, generate, generateImage, generateObject, getErrorMessage, getHttpStatusCode, getProviderMetadata, getRegisteredModelCapabilities, getRetryAfterSecondsFromError, getString, isAbortErrorByName, isRateLimitStatus, isTransientUnavailableStatus, parseRetryAfterSeconds, resultToMessage, safeParseJsonObject, stream, streamObject, stripModelDateSuffix, supportsInputModality, supportsOutputModality, validateInputModalities, wrapChatModel, wrapEmbeddingModel, wrapImageModel, zodSchemaToJsonSchema };
package/dist/index.js CHANGED
@@ -15,6 +15,25 @@ var ValidationError = class extends CoreAIError {
15
15
  this.name = "ValidationError";
16
16
  }
17
17
  };
18
+ var UnsupportedInputModalityError = class extends ValidationError {
19
+ requestedModalities;
20
+ supportedModalities;
21
+ unsupportedModalities;
22
+ constructor(options) {
23
+ const unsupported = options.unsupportedModalities.join(", ");
24
+ const supported = options.supportedModalities.join(", ") || "(none)";
25
+ const modalityWord = options.unsupportedModalities.length === 1 ? "modality" : "modalities";
26
+ super(
27
+ `${options.providerId} model "${options.modelId}" does not support input ${modalityWord}: ${unsupported}. Supported: ${supported}`,
28
+ void 0,
29
+ options.providerId
30
+ );
31
+ this.name = "UnsupportedInputModalityError";
32
+ this.requestedModalities = options.requestedModalities;
33
+ this.supportedModalities = options.supportedModalities;
34
+ this.unsupportedModalities = options.unsupportedModalities;
35
+ }
36
+ };
18
37
  var AbortedError = class extends CoreAIError {
19
38
  constructor(cause, provider) {
20
39
  super("operation aborted", cause, provider);
@@ -262,6 +281,63 @@ function clampReasoningEffort(effort, supportedEfforts) {
262
281
  }
263
282
  return best;
264
283
  }
284
+ var TEXT_ONLY_MODALITIES = {
285
+ input: ["text"],
286
+ output: ["text"]
287
+ };
288
+ var MULTIMODAL_INPUT_MODALITIES = {
289
+ input: ["text", "image", "file"],
290
+ output: ["text"]
291
+ };
292
+ function supportsInputModality(capabilities, modality) {
293
+ return capabilities.modalities.input.includes(modality);
294
+ }
295
+ function supportsOutputModality(capabilities, modality) {
296
+ return capabilities.modalities.output.includes(modality);
297
+ }
298
+
299
+ // src/validate-input-modalities.ts
300
+ function validateInputModalities({
301
+ messages,
302
+ capabilities,
303
+ modelId,
304
+ providerId
305
+ }) {
306
+ const requestedModalities = collectRequestedInputModalities(messages);
307
+ if (requestedModalities.length === 0) {
308
+ return;
309
+ }
310
+ const supportedModalities = capabilities.modalities.input;
311
+ const unsupportedModalities = requestedModalities.filter(
312
+ (modality) => !supportedModalities.includes(modality)
313
+ );
314
+ if (unsupportedModalities.length === 0) {
315
+ return;
316
+ }
317
+ throw new UnsupportedInputModalityError({
318
+ modelId,
319
+ providerId,
320
+ requestedModalities,
321
+ supportedModalities,
322
+ unsupportedModalities
323
+ });
324
+ }
325
+ function collectRequestedInputModalities(messages) {
326
+ const requested = /* @__PURE__ */ new Set();
327
+ for (const message of messages) {
328
+ if (message.role !== "user") {
329
+ continue;
330
+ }
331
+ if (typeof message.content === "string") {
332
+ requested.add("text");
333
+ continue;
334
+ }
335
+ for (const part of message.content) {
336
+ requested.add(part.type);
337
+ }
338
+ }
339
+ return [...requested];
340
+ }
265
341
 
266
342
  // src/model-capabilities-registry.ts
267
343
  var UNKNOWN_MODEL = /* @__PURE__ */ Symbol("unknown-model");
@@ -667,14 +743,15 @@ function createChatStream(source, options = {}) {
667
743
  }
668
744
  textBuffer += text;
669
745
  };
670
- const appendToolCall = (toolCall) => {
746
+ const appendToolCall = (toolCall, providerMetadata) => {
671
747
  flushText();
672
748
  insideText = false;
673
749
  flushReasoning();
674
750
  insideReasoning = false;
675
751
  parts.push({
676
752
  type: "tool-call",
677
- toolCall
753
+ toolCall,
754
+ ...providerMetadata ? { providerMetadata } : {}
678
755
  });
679
756
  };
680
757
  const setFinish = (event) => {
@@ -730,7 +807,7 @@ function createChatStream(source, options = {}) {
730
807
  endText(event.metadata);
731
808
  break;
732
809
  case "tool-call-end":
733
- appendToolCall(event.toolCall);
810
+ appendToolCall(event.toolCall, event.providerMetadata);
734
811
  break;
735
812
  case "finish":
736
813
  setFinish(event);
@@ -889,6 +966,7 @@ export {
889
966
  AbortedError,
890
967
  ContextLengthExceededError,
891
968
  CoreAIError,
969
+ MULTIMODAL_INPUT_MODALITIES,
892
970
  ModelOverloadedError,
893
971
  ProviderError,
894
972
  RateLimitError,
@@ -899,7 +977,9 @@ export {
899
977
  StructuredOutputNoObjectGeneratedError,
900
978
  StructuredOutputParseError,
901
979
  StructuredOutputValidationError,
980
+ TEXT_ONLY_MODALITIES,
902
981
  UNKNOWN_MODEL,
982
+ UnsupportedInputModalityError,
903
983
  ValidationError,
904
984
  asObject,
905
985
  asRecord,
@@ -927,6 +1007,9 @@ export {
927
1007
  stream,
928
1008
  streamObject,
929
1009
  stripModelDateSuffix,
1010
+ supportsInputModality,
1011
+ supportsOutputModality,
1012
+ validateInputModalities,
930
1013
  wrapChatModel,
931
1014
  wrapEmbeddingModel,
932
1015
  wrapImageModel,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@core-ai/core-ai",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "description": "Type-safe LLM abstraction layer over native provider SDKs",
5
5
  "license": "MIT",
6
6
  "author": "Omnifact (https://omnifact.ai)",