@core-ai/core-ai 0.5.0 → 0.6.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/README.md CHANGED
@@ -15,6 +15,7 @@ Provider packages are published separately:
15
15
  - `@core-ai/openai`
16
16
  - `@core-ai/anthropic`
17
17
  - `@core-ai/google-genai`
18
+ - `@core-ai/mistral`
18
19
 
19
20
  ## Usage
20
21
 
package/dist/index.d.ts CHANGED
@@ -42,7 +42,18 @@ type AssistantTextPart = {
42
42
  type ReasoningPart = {
43
43
  type: 'reasoning';
44
44
  text: string;
45
- providerMetadata?: Record<string, unknown>;
45
+ /**
46
+ * Provider-namespaced metadata for this reasoning block. The top-level key is
47
+ * the provider identifier (e.g. `'anthropic'`, `'openai'`), which also serves as
48
+ * the ownership discriminator: an adapter checks for the presence of its own key
49
+ * to detect cross-provider blocks. Cross-provider blocks are downgraded to plain
50
+ * text (preserving context) rather than forwarding opaque metadata that would
51
+ * cause an API error on the receiving provider.
52
+ *
53
+ * @example Anthropic: `{ anthropic: { signature: '...' } }`
54
+ * @example OpenAI: `{ openai: { encryptedContent: '...' } }`
55
+ */
56
+ providerMetadata?: Record<string, Record<string, unknown>>;
46
57
  };
47
58
  type ToolCallPart = {
48
59
  type: 'tool-call';
@@ -78,26 +89,31 @@ type ChatModel = {
78
89
  readonly provider: string;
79
90
  readonly modelId: string;
80
91
  generate(options: GenerateOptions): Promise<GenerateResult>;
81
- stream(options: GenerateOptions): Promise<StreamResult>;
92
+ stream(options: GenerateOptions): Promise<ChatStream>;
82
93
  generateObject<TSchema extends z.ZodType>(options: GenerateObjectOptions<TSchema>): Promise<GenerateObjectResult<TSchema>>;
83
- streamObject<TSchema extends z.ZodType>(options: StreamObjectOptions<TSchema>): Promise<StreamObjectResult<TSchema>>;
94
+ streamObject<TSchema extends z.ZodType>(options: StreamObjectOptions<TSchema>): Promise<ObjectStream<TSchema>>;
84
95
  };
85
- type ModelConfig = {
96
+ interface GenerateProviderOptions {
97
+ [key: string]: Record<string, unknown> | undefined;
98
+ }
99
+ interface EmbedProviderOptions {
100
+ [key: string]: Record<string, unknown> | undefined;
101
+ }
102
+ interface ImageProviderOptions {
103
+ [key: string]: Record<string, unknown> | undefined;
104
+ }
105
+ type BaseGenerateOptions = {
106
+ messages: Message[];
86
107
  temperature?: number;
87
108
  maxTokens?: number;
88
109
  topP?: number;
89
- stopSequences?: string[];
90
- frequencyPenalty?: number;
91
- presencePenalty?: number;
92
- };
93
- type GenerateOptions = {
94
- messages: Message[];
95
110
  reasoning?: ReasoningConfig;
111
+ providerOptions?: GenerateProviderOptions;
112
+ signal?: AbortSignal;
113
+ };
114
+ type GenerateOptions = BaseGenerateOptions & {
96
115
  tools?: ToolSet;
97
116
  toolChoice?: ToolChoice;
98
- config?: ModelConfig;
99
- providerOptions?: Record<string, unknown>;
100
- signal?: AbortSignal;
101
117
  };
102
118
  type GenerateResult = {
103
119
  parts: AssistantContentPart[];
@@ -107,15 +123,10 @@ type GenerateResult = {
107
123
  finishReason: FinishReason;
108
124
  usage: ChatUsage;
109
125
  };
110
- type GenerateObjectOptions<TSchema extends z.ZodType> = {
111
- messages: Message[];
126
+ type GenerateObjectOptions<TSchema extends z.ZodType> = BaseGenerateOptions & {
112
127
  schema: TSchema;
113
128
  schemaName?: string;
114
129
  schemaDescription?: string;
115
- reasoning?: ReasoningConfig;
116
- config?: ModelConfig;
117
- providerOptions?: Record<string, unknown>;
118
- signal?: AbortSignal;
119
130
  };
120
131
  type StreamObjectOptions<TSchema extends z.ZodType> = GenerateObjectOptions<TSchema>;
121
132
  type GenerateObjectResult<TSchema extends z.ZodType> = {
@@ -170,6 +181,7 @@ type StreamEvent = {
170
181
  text: string;
171
182
  } | {
172
183
  type: 'reasoning-end';
184
+ providerMetadata?: Record<string, Record<string, unknown>>;
173
185
  } | {
174
186
  type: 'text-delta';
175
187
  text: string;
@@ -189,8 +201,21 @@ type StreamEvent = {
189
201
  finishReason: FinishReason;
190
202
  usage: ChatUsage;
191
203
  };
192
- type StreamResult = AsyncIterable<StreamEvent> & {
193
- toResponse(): Promise<GenerateResult>;
204
+ /**
205
+ * Handle for a single in-flight chat streaming operation.
206
+ *
207
+ * The handle is replayable: iterating after some or all events have already
208
+ * arrived replays the buffered event history before waiting for later events.
209
+ *
210
+ * `result` resolves with the aggregated final response when the operation
211
+ * completes successfully, and rejects on abort or upstream failure.
212
+ *
213
+ * `events` always resolves with all observed events up to the terminal point,
214
+ * including abort and failure cases.
215
+ */
216
+ type ChatStream = AsyncIterable<StreamEvent> & {
217
+ readonly result: Promise<GenerateResult>;
218
+ readonly events: Promise<readonly StreamEvent[]>;
194
219
  };
195
220
  type ObjectStreamEvent<TSchema extends z.ZodType> = {
196
221
  type: 'object-delta';
@@ -203,8 +228,16 @@ type ObjectStreamEvent<TSchema extends z.ZodType> = {
203
228
  finishReason: FinishReason;
204
229
  usage: ChatUsage;
205
230
  };
206
- type StreamObjectResult<TSchema extends z.ZodType> = AsyncIterable<ObjectStreamEvent<TSchema>> & {
207
- toResponse(): Promise<GenerateObjectResult<TSchema>>;
231
+ /**
232
+ * Handle for a single in-flight structured object streaming operation.
233
+ *
234
+ * The lifecycle semantics mirror `ChatStream`: iteration is replayable,
235
+ * `result` settles independently of event consumption, and `events` resolves
236
+ * with the observed history.
237
+ */
238
+ type ObjectStream<TSchema extends z.ZodType> = AsyncIterable<ObjectStreamEvent<TSchema>> & {
239
+ readonly result: Promise<GenerateObjectResult<TSchema>>;
240
+ readonly events: Promise<readonly ObjectStreamEvent<TSchema>[]>;
208
241
  };
209
242
  type EmbeddingModel = {
210
243
  readonly provider: string;
@@ -214,7 +247,7 @@ type EmbeddingModel = {
214
247
  type EmbedOptions = {
215
248
  input: string | string[];
216
249
  dimensions?: number;
217
- providerOptions?: Record<string, unknown>;
250
+ providerOptions?: EmbedProviderOptions;
218
251
  };
219
252
  type EmbedResult = {
220
253
  embeddings: number[][];
@@ -237,7 +270,7 @@ type ImageGenerateOptions = {
237
270
  prompt: string;
238
271
  n?: number;
239
272
  size?: string;
240
- providerOptions?: Record<string, unknown>;
273
+ providerOptions?: ImageProviderOptions;
241
274
  };
242
275
  type ImageGenerateResult = {
243
276
  images: GeneratedImage[];
@@ -252,6 +285,9 @@ declare class LLMError extends Error {
252
285
  readonly cause?: unknown;
253
286
  constructor(message: string, cause?: unknown);
254
287
  }
288
+ declare class StreamAbortedError extends LLMError {
289
+ constructor(message?: string, cause?: unknown);
290
+ }
255
291
  declare class ProviderError extends LLMError {
256
292
  readonly provider: string;
257
293
  readonly statusCode?: number;
@@ -298,15 +334,21 @@ declare function generateObject<TSchema extends z.ZodType>(params: GenerateObjec
298
334
  type StreamParams = GenerateOptions & {
299
335
  model: ChatModel;
300
336
  };
301
- declare function stream(params: StreamParams): Promise<StreamResult>;
337
+ declare function stream(params: StreamParams): Promise<ChatStream>;
302
338
 
303
339
  type StreamObjectParams<TSchema extends z.ZodType> = StreamObjectOptions<TSchema> & {
304
340
  model: ChatModel;
305
341
  };
306
- declare function streamObject<TSchema extends z.ZodType>(params: StreamObjectParams<TSchema>): Promise<StreamObjectResult<TSchema>>;
307
- declare function createObjectStreamResult<TSchema extends z.ZodType>(source: AsyncIterable<ObjectStreamEvent<TSchema>>): StreamObjectResult<TSchema>;
342
+ declare function streamObject<TSchema extends z.ZodType>(params: StreamObjectParams<TSchema>): Promise<ObjectStream<TSchema>>;
343
+ declare function createObjectStream<TSchema extends z.ZodType>(source: AsyncIterable<ObjectStreamEvent<TSchema>>, options?: {
344
+ signal?: AbortSignal;
345
+ }): ObjectStream<TSchema>;
346
+
347
+ declare function createChatStream(source: AsyncIterable<StreamEvent> | (() => Promise<AsyncIterable<StreamEvent>>), options?: {
348
+ signal?: AbortSignal;
349
+ }): ChatStream;
308
350
 
309
- declare function createStreamResult(source: AsyncIterable<StreamEvent>): StreamResult;
351
+ declare function getProviderMetadata<T extends Record<string, unknown>>(providerMetadata: Record<string, Record<string, unknown>> | undefined, provider: string): T | undefined;
310
352
 
311
353
  type EmbedParams = EmbedOptions & {
312
354
  model: EmbeddingModel;
@@ -318,4 +360,4 @@ type GenerateImageParams = ImageGenerateOptions & {
318
360
  };
319
361
  declare function generateImage(params: GenerateImageParams): Promise<ImageGenerateResult>;
320
362
 
321
- export { type AssistantContentPart, type AssistantMessage, type AssistantTextPart, type ChatInputTokenDetails, type ChatModel, type ChatOutputTokenDetails, type ChatUsage, type EmbedOptions, type EmbedResult, type EmbeddingModel, type EmbeddingUsage, type FilePart, type FinishReason, type GenerateObjectOptions, type GenerateObjectResult, type GenerateOptions, type GenerateResult, type GeneratedImage, type ImageGenerateOptions, type ImageGenerateResult, type ImageModel, type ImagePart, LLMError, type Message, type ModelConfig, type ObjectStreamEvent, ProviderError, type ReasoningConfig, type ReasoningEffort, type ReasoningPart, type StreamEvent, type StreamObjectOptions, type StreamObjectResult, type StreamResult, StructuredOutputError, StructuredOutputNoObjectGeneratedError, StructuredOutputParseError, StructuredOutputValidationError, type SystemMessage, type TextPart, type ToolCall, type ToolCallPart, type ToolChoice, type ToolDefinition, type ToolResultMessage, type ToolSet, type UserContentPart, type UserMessage, assistantMessage, createObjectStreamResult, createStreamResult, defineTool, embed, generate, generateImage, generateObject, resultToMessage, stream, streamObject };
363
+ export { type AssistantContentPart, type AssistantMessage, type AssistantTextPart, type BaseGenerateOptions, type ChatInputTokenDetails, type ChatModel, type ChatOutputTokenDetails, type ChatStream, type ChatUsage, type EmbedOptions, type EmbedProviderOptions, type EmbedResult, type EmbeddingModel, 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 ImagePart, type ImageProviderOptions, LLMError, type Message, type ObjectStream, type ObjectStreamEvent, ProviderError, type ReasoningConfig, type ReasoningEffort, type ReasoningPart, 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, type UserContentPart, type UserMessage, assistantMessage, createChatStream, createObjectStream, defineTool, embed, generate, generateImage, generateObject, getProviderMetadata, resultToMessage, stream, streamObject };
package/dist/index.js CHANGED
@@ -7,6 +7,12 @@ var LLMError = class extends Error {
7
7
  this.cause = cause;
8
8
  }
9
9
  };
10
+ var StreamAbortedError = class extends LLMError {
11
+ constructor(message = "stream aborted", cause) {
12
+ super(message, cause);
13
+ this.name = "StreamAbortedError";
14
+ }
15
+ };
10
16
  var ProviderError = class extends LLMError {
11
17
  provider;
12
18
  statusCode;
@@ -68,156 +74,306 @@ function assistantMessage(content) {
68
74
  };
69
75
  }
70
76
 
71
- // src/generate.ts
72
- async function generate(params) {
73
- if (params.messages.length === 0) {
77
+ // src/assertions.ts
78
+ function assertNonEmptyMessages(messages) {
79
+ if (messages.length === 0) {
74
80
  throw new LLMError("messages must not be empty");
75
81
  }
82
+ }
83
+ function assertNonEmptyEmbedInput(input) {
84
+ if (typeof input === "string" && input.length === 0) {
85
+ throw new LLMError("input must not be empty");
86
+ }
87
+ if (Array.isArray(input) && input.length === 0) {
88
+ throw new LLMError("input must not be empty");
89
+ }
90
+ }
91
+
92
+ // src/generate.ts
93
+ async function generate(params) {
94
+ assertNonEmptyMessages(params.messages);
76
95
  const { model, ...options } = params;
77
96
  return model.generate(options);
78
97
  }
79
98
 
80
99
  // src/generate-object.ts
81
100
  async function generateObject(params) {
82
- if (params.messages.length === 0) {
83
- throw new LLMError("messages must not be empty");
84
- }
101
+ assertNonEmptyMessages(params.messages);
85
102
  const { model, ...options } = params;
86
103
  return model.generateObject(options);
87
104
  }
88
105
 
89
106
  // src/stream-chat.ts
90
107
  async function stream(params) {
91
- if (params.messages.length === 0) {
92
- throw new LLMError("messages must not be empty");
93
- }
108
+ assertNonEmptyMessages(params.messages);
94
109
  const { model, ...options } = params;
95
110
  return model.stream(options);
96
111
  }
97
112
 
98
- // src/stream-object.ts
99
- async function streamObject(params) {
100
- if (params.messages.length === 0) {
101
- throw new LLMError("messages must not be empty");
113
+ // src/base-stream.ts
114
+ function createStream(options) {
115
+ const { source, reduceEvent, finalizeResult, signal } = options;
116
+ const iterator = source[Symbol.asyncIterator]();
117
+ const bufferedEvents = [];
118
+ let terminalState = { status: "running" };
119
+ let resolveResult;
120
+ let rejectResult;
121
+ let resolveEvents;
122
+ const result = new Promise((resolve, reject) => {
123
+ resolveResult = resolve;
124
+ rejectResult = reject;
125
+ });
126
+ void result.catch(() => {
127
+ });
128
+ const events = new Promise((resolve) => {
129
+ resolveEvents = resolve;
130
+ });
131
+ const waiters = /* @__PURE__ */ new Set();
132
+ let closeSourceIteratorPromise;
133
+ function notifyWaiters() {
134
+ for (const waiter of waiters) {
135
+ waiter();
136
+ }
137
+ waiters.clear();
102
138
  }
103
- const { model, ...options } = params;
104
- return model.streamObject(options);
105
- }
106
- function createObjectStreamResult(source) {
107
- let resolveResponse;
108
- let rejectResponse;
109
- const responsePromise = new Promise(
110
- (resolve, reject) => {
111
- resolveResponse = resolve;
112
- rejectResponse = reject;
139
+ function resolveWhenUpdated() {
140
+ if (terminalState.status !== "running") {
141
+ return Promise.resolve();
113
142
  }
114
- );
115
- let iteratorCreated = false;
116
- async function* iterate() {
117
- let objectResult;
118
- let finishReason = "unknown";
119
- let usage = {
120
- inputTokens: 0,
121
- outputTokens: 0,
122
- inputTokenDetails: {
123
- cacheReadTokens: 0,
124
- cacheWriteTokens: 0
125
- },
126
- outputTokenDetails: {}
143
+ return new Promise((resolve) => {
144
+ waiters.add(resolve);
145
+ });
146
+ }
147
+ function cleanupSignalListener() {
148
+ signal?.removeEventListener("abort", abortStream);
149
+ }
150
+ function settleCompleted(finalResult) {
151
+ if (terminalState.status !== "running") {
152
+ return;
153
+ }
154
+ terminalState = {
155
+ status: "completed",
156
+ result: finalResult
127
157
  };
158
+ cleanupSignalListener();
159
+ resolveResult?.(finalResult);
160
+ resolveEvents?.([...bufferedEvents]);
161
+ notifyWaiters();
162
+ }
163
+ function settleRejected(error) {
164
+ if (terminalState.status !== "running") {
165
+ return;
166
+ }
167
+ terminalState = {
168
+ status: "rejected",
169
+ error
170
+ };
171
+ cleanupSignalListener();
172
+ rejectResult?.(error);
173
+ resolveEvents?.([...bufferedEvents]);
174
+ notifyWaiters();
175
+ }
176
+ function closeSourceIterator() {
177
+ if (closeSourceIteratorPromise) {
178
+ return closeSourceIteratorPromise;
179
+ }
180
+ closeSourceIteratorPromise = (async () => {
181
+ try {
182
+ await iterator.return?.();
183
+ } catch {
184
+ }
185
+ })();
186
+ return closeSourceIteratorPromise;
187
+ }
188
+ function abortStream() {
189
+ if (terminalState.status !== "running") {
190
+ return;
191
+ }
192
+ settleRejected(new StreamAbortedError("stream aborted"));
193
+ void closeSourceIterator();
194
+ }
195
+ if (signal) {
196
+ if (signal.aborted) {
197
+ abortStream();
198
+ } else {
199
+ signal.addEventListener("abort", abortStream, { once: true });
200
+ }
201
+ }
202
+ async function pump() {
128
203
  try {
129
- for await (const event of source) {
130
- if (event.type === "object") {
131
- objectResult = event.object;
132
- } else if (event.type === "finish") {
133
- finishReason = event.finishReason;
134
- usage = event.usage;
204
+ while (terminalState.status === "running") {
205
+ const next = await iterator.next();
206
+ if (terminalState.status !== "running") {
207
+ await closeSourceIterator();
208
+ return;
135
209
  }
136
- yield event;
137
- }
138
- if (objectResult === void 0) {
139
- throw new LLMError(
140
- "object stream completed without emitting a final object"
141
- );
210
+ if (next.done) {
211
+ try {
212
+ settleCompleted(finalizeResult());
213
+ } catch (error) {
214
+ settleRejected(error);
215
+ }
216
+ return;
217
+ }
218
+ bufferedEvents.push(next.value);
219
+ notifyWaiters();
220
+ reduceEvent(next.value);
142
221
  }
143
- resolveResponse?.({
144
- object: objectResult,
145
- finishReason,
146
- usage
147
- });
148
222
  } catch (error) {
149
- rejectResponse?.(error);
150
- throw error;
223
+ settleRejected(error);
224
+ await closeSourceIterator();
151
225
  }
152
226
  }
153
- const generator = iterate();
227
+ void pump();
154
228
  return {
155
229
  [Symbol.asyncIterator]() {
156
- if (iteratorCreated) {
157
- throw new Error("Stream can only be iterated once");
158
- }
159
- iteratorCreated = true;
160
- return generator;
161
- },
162
- toResponse() {
163
- if (!iteratorCreated) {
164
- iteratorCreated = true;
165
- (async () => {
166
- try {
167
- for await (const _event of generator) {
230
+ let index = 0;
231
+ let closed = false;
232
+ return {
233
+ async next() {
234
+ if (closed) {
235
+ return {
236
+ done: true,
237
+ value: void 0
238
+ };
239
+ }
240
+ while (!closed && index >= bufferedEvents.length) {
241
+ if (terminalState.status === "completed") {
242
+ return {
243
+ done: true,
244
+ value: void 0
245
+ };
246
+ }
247
+ if (terminalState.status === "rejected") {
248
+ throw terminalState.error;
168
249
  }
169
- } catch {
250
+ await resolveWhenUpdated();
170
251
  }
171
- })();
172
- }
173
- return responsePromise;
174
- }
252
+ if (closed) {
253
+ return {
254
+ done: true,
255
+ value: void 0
256
+ };
257
+ }
258
+ const value = bufferedEvents[index];
259
+ index += 1;
260
+ return {
261
+ done: false,
262
+ value
263
+ };
264
+ },
265
+ async return() {
266
+ closed = true;
267
+ notifyWaiters();
268
+ return {
269
+ done: true,
270
+ value: void 0
271
+ };
272
+ }
273
+ };
274
+ },
275
+ result,
276
+ events
175
277
  };
176
278
  }
177
279
 
178
- // src/stream.ts
179
- function createStreamResult(source) {
180
- let resolveResponse;
181
- const responsePromise = new Promise((resolve) => {
182
- resolveResponse = resolve;
183
- });
184
- let iteratorCreated = false;
185
- async function* iterate() {
186
- const parts = [];
187
- let textBuffer = "";
188
- let reasoningBuffer = "";
189
- let insideReasoning = false;
190
- let finishReason = "unknown";
191
- let usage = {
192
- inputTokens: 0,
193
- outputTokens: 0,
194
- inputTokenDetails: {
195
- cacheReadTokens: 0,
196
- cacheWriteTokens: 0
197
- },
198
- outputTokenDetails: {}
199
- };
200
- const flushText = () => {
201
- if (textBuffer.length === 0) {
202
- return;
280
+ // src/stream-object.ts
281
+ async function streamObject(params) {
282
+ assertNonEmptyMessages(params.messages);
283
+ const { model, ...options } = params;
284
+ return model.streamObject(options);
285
+ }
286
+ function createObjectStream(source, options = {}) {
287
+ const { signal } = options;
288
+ let objectState = {
289
+ status: "pending"
290
+ };
291
+ let finishReason = "unknown";
292
+ let usage = {
293
+ inputTokens: 0,
294
+ outputTokens: 0,
295
+ inputTokenDetails: {
296
+ cacheReadTokens: 0,
297
+ cacheWriteTokens: 0
298
+ },
299
+ outputTokenDetails: {}
300
+ };
301
+ return createStream({
302
+ source,
303
+ signal,
304
+ reduceEvent(event) {
305
+ if (event.type === "object") {
306
+ objectState = {
307
+ status: "ready",
308
+ object: event.object
309
+ };
310
+ } else if (event.type === "finish") {
311
+ finishReason = event.finishReason;
312
+ usage = event.usage;
203
313
  }
204
- parts.push({
205
- type: "text",
206
- text: textBuffer
207
- });
208
- textBuffer = "";
209
- };
210
- const flushReasoning = () => {
211
- if (reasoningBuffer.length === 0) {
212
- return;
314
+ },
315
+ finalizeResult() {
316
+ if (objectState.status !== "ready") {
317
+ throw new LLMError(
318
+ "object stream completed without emitting a final object"
319
+ );
213
320
  }
214
- parts.push({
215
- type: "reasoning",
216
- text: reasoningBuffer
217
- });
218
- reasoningBuffer = "";
219
- };
220
- for await (const event of source) {
321
+ return {
322
+ object: objectState.object,
323
+ finishReason,
324
+ usage
325
+ };
326
+ }
327
+ });
328
+ }
329
+
330
+ // src/stream.ts
331
+ function createChatStream(source, options = {}) {
332
+ const { signal } = options;
333
+ const resolvedSource = typeof source === "function" ? (async function* () {
334
+ yield* await source();
335
+ })() : source;
336
+ const parts = [];
337
+ let textBuffer = "";
338
+ let reasoningBuffer = "";
339
+ let reasoningProviderMetadata;
340
+ let insideReasoning = false;
341
+ let finishReason = "unknown";
342
+ let usage = {
343
+ inputTokens: 0,
344
+ outputTokens: 0,
345
+ inputTokenDetails: {
346
+ cacheReadTokens: 0,
347
+ cacheWriteTokens: 0
348
+ },
349
+ outputTokenDetails: {}
350
+ };
351
+ const flushText = () => {
352
+ if (textBuffer.length === 0) {
353
+ return;
354
+ }
355
+ parts.push({
356
+ type: "text",
357
+ text: textBuffer
358
+ });
359
+ textBuffer = "";
360
+ };
361
+ const flushReasoning = () => {
362
+ if (reasoningBuffer.length === 0 && reasoningProviderMetadata === void 0) {
363
+ return;
364
+ }
365
+ parts.push({
366
+ type: "reasoning",
367
+ text: reasoningBuffer,
368
+ ...reasoningProviderMetadata ? { providerMetadata: reasoningProviderMetadata } : {}
369
+ });
370
+ reasoningBuffer = "";
371
+ reasoningProviderMetadata = void 0;
372
+ };
373
+ return createStream({
374
+ source: resolvedSource,
375
+ signal,
376
+ reduceEvent(event) {
221
377
  if (event.type === "reasoning-start") {
222
378
  flushText();
223
379
  flushReasoning();
@@ -229,6 +385,7 @@ function createStreamResult(source) {
229
385
  }
230
386
  reasoningBuffer += event.text;
231
387
  } else if (event.type === "reasoning-end") {
388
+ reasoningProviderMetadata = event.providerMetadata;
232
389
  flushReasoning();
233
390
  insideReasoning = false;
234
391
  } else if (event.type === "text-delta") {
@@ -249,55 +406,37 @@ function createStreamResult(source) {
249
406
  finishReason = event.finishReason;
250
407
  usage = event.usage;
251
408
  }
252
- yield event;
253
- }
254
- flushText();
255
- flushReasoning();
256
- const content = parts.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
257
- const reasoning = parts.flatMap((part) => part.type === "reasoning" ? [part.text] : []).join("");
258
- const toolCalls = parts.flatMap(
259
- (part) => part.type === "tool-call" ? [part.toolCall] : []
260
- );
261
- resolveResponse?.({
262
- parts,
263
- content: content.length > 0 ? content : null,
264
- reasoning: reasoning.length > 0 ? reasoning : null,
265
- toolCalls,
266
- finishReason,
267
- usage
268
- });
269
- }
270
- const generator = iterate();
271
- return {
272
- [Symbol.asyncIterator]() {
273
- if (iteratorCreated) {
274
- throw new Error("Stream can only be iterated once");
275
- }
276
- iteratorCreated = true;
277
- return generator;
278
409
  },
279
- toResponse() {
280
- if (!iteratorCreated) {
281
- iteratorCreated = true;
282
- (async () => {
283
- for await (const _event of generator) {
284
- }
285
- })();
286
- }
287
- return responsePromise;
410
+ finalizeResult() {
411
+ flushText();
412
+ flushReasoning();
413
+ const content = parts.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
414
+ const reasoning = parts.flatMap(
415
+ (part) => part.type === "reasoning" ? [part.text] : []
416
+ ).join("");
417
+ const toolCalls = parts.flatMap(
418
+ (part) => part.type === "tool-call" ? [part.toolCall] : []
419
+ );
420
+ return {
421
+ parts,
422
+ content: content.length > 0 ? content : null,
423
+ reasoning: reasoning.length > 0 ? reasoning : null,
424
+ toolCalls,
425
+ finishReason,
426
+ usage
427
+ };
288
428
  }
289
- };
429
+ });
430
+ }
431
+
432
+ // src/provider-metadata.ts
433
+ function getProviderMetadata(providerMetadata, provider) {
434
+ return providerMetadata?.[provider];
290
435
  }
291
436
 
292
437
  // src/embed.ts
293
438
  async function embed(params) {
294
- const { input } = params;
295
- if (typeof input === "string" && input.length === 0) {
296
- throw new LLMError("input must not be empty");
297
- }
298
- if (Array.isArray(input) && input.length === 0) {
299
- throw new LLMError("input must not be empty");
300
- }
439
+ assertNonEmptyEmbedInput(params.input);
301
440
  const { model, ...options } = params;
302
441
  return model.embed(options);
303
442
  }
@@ -313,18 +452,20 @@ async function generateImage(params) {
313
452
  export {
314
453
  LLMError,
315
454
  ProviderError,
455
+ StreamAbortedError,
316
456
  StructuredOutputError,
317
457
  StructuredOutputNoObjectGeneratedError,
318
458
  StructuredOutputParseError,
319
459
  StructuredOutputValidationError,
320
460
  assistantMessage,
321
- createObjectStreamResult,
322
- createStreamResult,
461
+ createChatStream,
462
+ createObjectStream,
323
463
  defineTool,
324
464
  embed,
325
465
  generate,
326
466
  generateImage,
327
467
  generateObject,
468
+ getProviderMetadata,
328
469
  resultToMessage,
329
470
  stream,
330
471
  streamObject
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@core-ai/core-ai",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Type-safe LLM abstraction layer over native provider SDKs",
5
5
  "license": "MIT",
6
6
  "author": "Omnifact (https://omnifact.ai)",
@@ -39,7 +39,6 @@
39
39
  "build": "tsup",
40
40
  "lint": "eslint src/ --max-warnings 0",
41
41
  "check-types": "tsc --noEmit",
42
- "prepublishOnly": "npm run build",
43
42
  "test": "vitest run",
44
43
  "test:watch": "vitest"
45
44
  },
@@ -48,8 +47,9 @@
48
47
  "zod-to-json-schema": "^3.25.1"
49
48
  },
50
49
  "devDependencies": {
51
- "@core-ai/eslint-config": "^0.0.0",
52
- "@core-ai/typescript-config": "^0.0.0",
50
+ "@core-ai/eslint-config": "*",
51
+ "@core-ai/testing": "*",
52
+ "@core-ai/typescript-config": "*",
53
53
  "typescript": "^5.7.3",
54
54
  "vitest": "^3.2.4"
55
55
  }