@core-ai/core-ai 0.2.1 → 0.4.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
@@ -62,6 +62,8 @@ type ChatModel = {
62
62
  readonly modelId: string;
63
63
  generate(options: GenerateOptions): Promise<GenerateResult>;
64
64
  stream(options: GenerateOptions): Promise<StreamResult>;
65
+ generateObject<TSchema extends z.ZodType>(options: GenerateObjectOptions<TSchema>): Promise<GenerateObjectResult<TSchema>>;
66
+ streamObject<TSchema extends z.ZodType>(options: StreamObjectOptions<TSchema>): Promise<StreamObjectResult<TSchema>>;
65
67
  };
66
68
  type ModelConfig = {
67
69
  temperature?: number;
@@ -85,32 +87,60 @@ type GenerateResult = {
85
87
  finishReason: FinishReason;
86
88
  usage: ChatUsage;
87
89
  };
90
+ type GenerateObjectOptions<TSchema extends z.ZodType> = {
91
+ messages: Message[];
92
+ schema: TSchema;
93
+ schemaName?: string;
94
+ schemaDescription?: string;
95
+ config?: ModelConfig;
96
+ providerOptions?: Record<string, unknown>;
97
+ signal?: AbortSignal;
98
+ };
99
+ type StreamObjectOptions<TSchema extends z.ZodType> = GenerateObjectOptions<TSchema>;
100
+ type GenerateObjectResult<TSchema extends z.ZodType> = {
101
+ object: z.infer<TSchema>;
102
+ finishReason: FinishReason;
103
+ usage: ChatUsage;
104
+ };
88
105
  type FinishReason = 'stop' | 'length' | 'tool-calls' | 'content-filter' | 'unknown';
89
106
  /**
90
107
  * Token usage reported by the model after a chat completion.
91
108
  *
92
- * `outputTokens` is the **total** output token count, including both visible
93
- * text and any internal reasoning/thinking the model performed.
94
- * `reasoningTokens` is the subset of `outputTokens` consumed by reasoning.
95
- * For non-reasoning models (or providers that don't report it separately)
96
- * this will be `0`.
109
+ * `inputTokens` is always the **total** input token count, including cached
110
+ * reads and cache writes. Anthropic's `input_tokens` is normalized by adding
111
+ * `cache_read_input_tokens` and `cache_creation_input_tokens`.
97
112
  *
98
- * Provider mapping:
99
- * - **OpenAI**: `reasoningTokens` comes from `completion_tokens_details.reasoning_tokens`.
100
- * - **Google Gemini**: `reasoningTokens` comes from `thoughtsTokenCount`;
101
- * `outputTokens` = `candidatesTokenCount + thoughtsTokenCount`.
102
- * - **Anthropic**: `reasoningTokens` is always `0` (thinking tokens are
103
- * included in `output_tokens` but not reported separately by the API).
113
+ * `outputTokens` is always the **total** output token count, including both
114
+ * visible text and internal reasoning.
115
+ *
116
+ * `inputTokenDetails` and `outputTokenDetails` provide provider-independent
117
+ * breakdowns for cache and reasoning accounting.
104
118
  */
105
119
  type ChatUsage = {
106
- /** Number of tokens in the input prompt. */
120
+ /** Total input tokens, including cached and cache-write tokens. */
107
121
  inputTokens: number;
108
122
  /** Total output tokens, including both visible text and reasoning. */
109
123
  outputTokens: number;
110
- /** Tokens consumed by internal reasoning/thinking. Subset of `outputTokens`. */
124
+ /** Breakdown of input token categories. */
125
+ inputTokenDetails: ChatInputTokenDetails;
126
+ /** Breakdown of output token categories. */
127
+ outputTokenDetails: ChatOutputTokenDetails;
128
+ };
129
+ type ChatInputTokenDetails = {
130
+ /** Input tokens served from a prior cache entry. Subset of `inputTokens`. */
131
+ cacheReadTokens: number;
132
+ /**
133
+ * Input tokens written to cache for future reuse. Subset of `inputTokens`.
134
+ * Only Anthropic reports this; other providers report `0`.
135
+ */
136
+ cacheWriteTokens: number;
137
+ };
138
+ type ChatOutputTokenDetails = {
139
+ /**
140
+ * Tokens consumed by internal reasoning/thinking. Subset of `outputTokens`.
141
+ * For non-reasoning models (or providers that don't report it), this is `0`.
142
+ */
111
143
  reasoningTokens: number;
112
- /** Sum of all tokens (`inputTokens + outputTokens`). */
113
- totalTokens: number;
114
144
  };
115
145
  type StreamEvent = {
116
146
  type: 'content-delta';
@@ -134,6 +164,20 @@ type StreamEvent = {
134
164
  type StreamResult = AsyncIterable<StreamEvent> & {
135
165
  toResponse(): Promise<GenerateResult>;
136
166
  };
167
+ type ObjectStreamEvent<TSchema extends z.ZodType> = {
168
+ type: 'object-delta';
169
+ text: string;
170
+ } | {
171
+ type: 'object';
172
+ object: z.infer<TSchema>;
173
+ } | {
174
+ type: 'finish';
175
+ finishReason: FinishReason;
176
+ usage: ChatUsage;
177
+ };
178
+ type StreamObjectResult<TSchema extends z.ZodType> = AsyncIterable<ObjectStreamEvent<TSchema>> & {
179
+ toResponse(): Promise<GenerateObjectResult<TSchema>>;
180
+ };
137
181
  type EmbeddingModel = {
138
182
  readonly provider: string;
139
183
  readonly modelId: string;
@@ -146,9 +190,14 @@ type EmbedOptions = {
146
190
  };
147
191
  type EmbedResult = {
148
192
  embeddings: number[][];
149
- usage: EmbeddingUsage;
193
+ /**
194
+ * Optional embedding usage metadata. Some providers/models do not expose
195
+ * token usage for embedding calls.
196
+ */
197
+ usage?: EmbeddingUsage;
150
198
  };
151
199
  type EmbeddingUsage = {
200
+ /** Number of tokens consumed by embedding input. */
152
201
  inputTokens: number;
153
202
  };
154
203
  type ImageModel = {
@@ -180,6 +229,25 @@ declare class ProviderError extends LLMError {
180
229
  readonly statusCode?: number;
181
230
  constructor(message: string, provider: string, statusCode?: number, cause?: unknown);
182
231
  }
232
+ type StructuredOutputErrorOptions = {
233
+ statusCode?: number;
234
+ cause?: unknown;
235
+ rawOutput?: string;
236
+ };
237
+ declare class StructuredOutputError extends ProviderError {
238
+ readonly rawOutput?: string;
239
+ constructor(message: string, provider: string, options?: StructuredOutputErrorOptions);
240
+ }
241
+ declare class StructuredOutputNoObjectGeneratedError extends StructuredOutputError {
242
+ constructor(message: string, provider: string, options?: StructuredOutputErrorOptions);
243
+ }
244
+ declare class StructuredOutputParseError extends StructuredOutputError {
245
+ constructor(message: string, provider: string, options?: StructuredOutputErrorOptions);
246
+ }
247
+ declare class StructuredOutputValidationError extends StructuredOutputError {
248
+ readonly issues: string[];
249
+ constructor(message: string, provider: string, issues: string[], options?: StructuredOutputErrorOptions);
250
+ }
183
251
 
184
252
  declare function defineTool(options: ToolDefinition): ToolDefinition;
185
253
 
@@ -188,11 +256,22 @@ type GenerateParams = GenerateOptions & {
188
256
  };
189
257
  declare function generate(params: GenerateParams): Promise<GenerateResult>;
190
258
 
259
+ type GenerateObjectParams<TSchema extends z.ZodType> = GenerateObjectOptions<TSchema> & {
260
+ model: ChatModel;
261
+ };
262
+ declare function generateObject<TSchema extends z.ZodType>(params: GenerateObjectParams<TSchema>): Promise<GenerateObjectResult<TSchema>>;
263
+
191
264
  type StreamParams = GenerateOptions & {
192
265
  model: ChatModel;
193
266
  };
194
267
  declare function stream(params: StreamParams): Promise<StreamResult>;
195
268
 
269
+ type StreamObjectParams<TSchema extends z.ZodType> = StreamObjectOptions<TSchema> & {
270
+ model: ChatModel;
271
+ };
272
+ declare function streamObject<TSchema extends z.ZodType>(params: StreamObjectParams<TSchema>): Promise<StreamObjectResult<TSchema>>;
273
+ declare function createObjectStreamResult<TSchema extends z.ZodType>(source: AsyncIterable<ObjectStreamEvent<TSchema>>): StreamObjectResult<TSchema>;
274
+
196
275
  declare function createStreamResult(source: AsyncIterable<StreamEvent>): StreamResult;
197
276
 
198
277
  type EmbedParams = EmbedOptions & {
@@ -205,4 +284,4 @@ type GenerateImageParams = ImageGenerateOptions & {
205
284
  };
206
285
  declare function generateImage(params: GenerateImageParams): Promise<ImageGenerateResult>;
207
286
 
208
- export { type AssistantMessage, type ChatModel, type ChatUsage, type EmbedOptions, type EmbedResult, type EmbeddingModel, type EmbeddingUsage, type FilePart, type FinishReason, type GenerateOptions, type GenerateResult, type GeneratedImage, type ImageGenerateOptions, type ImageGenerateResult, type ImageModel, type ImagePart, LLMError, type Message, type ModelConfig, ProviderError, type StreamEvent, type StreamResult, type SystemMessage, type TextPart, type ToolCall, type ToolChoice, type ToolDefinition, type ToolResultMessage, type ToolSet, type UserContentPart, type UserMessage, createStreamResult, defineTool, embed, generate, generateImage, stream };
287
+ export { type AssistantMessage, 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 StreamEvent, type StreamObjectOptions, type StreamObjectResult, type StreamResult, StructuredOutputError, StructuredOutputNoObjectGeneratedError, StructuredOutputParseError, StructuredOutputValidationError, type SystemMessage, type TextPart, type ToolCall, type ToolChoice, type ToolDefinition, type ToolResultMessage, type ToolSet, type UserContentPart, type UserMessage, createObjectStreamResult, createStreamResult, defineTool, embed, generate, generateImage, generateObject, stream, streamObject };
package/dist/index.js CHANGED
@@ -17,6 +17,34 @@ var ProviderError = class extends LLMError {
17
17
  this.statusCode = statusCode;
18
18
  }
19
19
  };
20
+ var StructuredOutputError = class extends ProviderError {
21
+ rawOutput;
22
+ constructor(message, provider, options = {}) {
23
+ super(message, provider, options.statusCode, options.cause);
24
+ this.name = "StructuredOutputError";
25
+ this.rawOutput = options.rawOutput;
26
+ }
27
+ };
28
+ var StructuredOutputNoObjectGeneratedError = class extends StructuredOutputError {
29
+ constructor(message, provider, options = {}) {
30
+ super(message, provider, options);
31
+ this.name = "StructuredOutputNoObjectGeneratedError";
32
+ }
33
+ };
34
+ var StructuredOutputParseError = class extends StructuredOutputError {
35
+ constructor(message, provider, options = {}) {
36
+ super(message, provider, options);
37
+ this.name = "StructuredOutputParseError";
38
+ }
39
+ };
40
+ var StructuredOutputValidationError = class extends StructuredOutputError {
41
+ issues;
42
+ constructor(message, provider, issues, options = {}) {
43
+ super(message, provider, options);
44
+ this.name = "StructuredOutputValidationError";
45
+ this.issues = issues;
46
+ }
47
+ };
20
48
 
21
49
  // src/tool.ts
22
50
  import { zodToJsonSchema } from "zod-to-json-schema";
@@ -33,6 +61,15 @@ async function generate(params) {
33
61
  return model.generate(options);
34
62
  }
35
63
 
64
+ // src/generate-object.ts
65
+ async function generateObject(params) {
66
+ if (params.messages.length === 0) {
67
+ throw new LLMError("messages must not be empty");
68
+ }
69
+ const { model, ...options } = params;
70
+ return model.generateObject(options);
71
+ }
72
+
36
73
  // src/stream-chat.ts
37
74
  async function stream(params) {
38
75
  if (params.messages.length === 0) {
@@ -42,6 +79,88 @@ async function stream(params) {
42
79
  return model.stream(options);
43
80
  }
44
81
 
82
+ // src/stream-object.ts
83
+ async function streamObject(params) {
84
+ if (params.messages.length === 0) {
85
+ throw new LLMError("messages must not be empty");
86
+ }
87
+ const { model, ...options } = params;
88
+ return model.streamObject(options);
89
+ }
90
+ function createObjectStreamResult(source) {
91
+ let resolveResponse;
92
+ let rejectResponse;
93
+ const responsePromise = new Promise(
94
+ (resolve, reject) => {
95
+ resolveResponse = resolve;
96
+ rejectResponse = reject;
97
+ }
98
+ );
99
+ let iteratorCreated = false;
100
+ async function* iterate() {
101
+ let objectResult;
102
+ let finishReason = "unknown";
103
+ let usage = {
104
+ inputTokens: 0,
105
+ outputTokens: 0,
106
+ inputTokenDetails: {
107
+ cacheReadTokens: 0,
108
+ cacheWriteTokens: 0
109
+ },
110
+ outputTokenDetails: {
111
+ reasoningTokens: 0
112
+ }
113
+ };
114
+ try {
115
+ for await (const event of source) {
116
+ if (event.type === "object") {
117
+ objectResult = event.object;
118
+ } else if (event.type === "finish") {
119
+ finishReason = event.finishReason;
120
+ usage = event.usage;
121
+ }
122
+ yield event;
123
+ }
124
+ if (objectResult === void 0) {
125
+ throw new LLMError(
126
+ "object stream completed without emitting a final object"
127
+ );
128
+ }
129
+ resolveResponse?.({
130
+ object: objectResult,
131
+ finishReason,
132
+ usage
133
+ });
134
+ } catch (error) {
135
+ rejectResponse?.(error);
136
+ throw error;
137
+ }
138
+ }
139
+ const generator = iterate();
140
+ return {
141
+ [Symbol.asyncIterator]() {
142
+ if (iteratorCreated) {
143
+ throw new Error("Stream can only be iterated once");
144
+ }
145
+ iteratorCreated = true;
146
+ return generator;
147
+ },
148
+ toResponse() {
149
+ if (!iteratorCreated) {
150
+ iteratorCreated = true;
151
+ (async () => {
152
+ try {
153
+ for await (const _event of generator) {
154
+ }
155
+ } catch {
156
+ }
157
+ })();
158
+ }
159
+ return responsePromise;
160
+ }
161
+ };
162
+ }
163
+
45
164
  // src/stream.ts
46
165
  function createStreamResult(source) {
47
166
  let resolveResponse;
@@ -56,8 +175,13 @@ function createStreamResult(source) {
56
175
  let usage = {
57
176
  inputTokens: 0,
58
177
  outputTokens: 0,
59
- reasoningTokens: 0,
60
- totalTokens: 0
178
+ inputTokenDetails: {
179
+ cacheReadTokens: 0,
180
+ cacheWriteTokens: 0
181
+ },
182
+ outputTokenDetails: {
183
+ reasoningTokens: 0
184
+ }
61
185
  };
62
186
  for await (const event of source) {
63
187
  if (event.type === "content-delta") {
@@ -123,10 +247,17 @@ async function generateImage(params) {
123
247
  export {
124
248
  LLMError,
125
249
  ProviderError,
250
+ StructuredOutputError,
251
+ StructuredOutputNoObjectGeneratedError,
252
+ StructuredOutputParseError,
253
+ StructuredOutputValidationError,
254
+ createObjectStreamResult,
126
255
  createStreamResult,
127
256
  defineTool,
128
257
  embed,
129
258
  generate,
130
259
  generateImage,
131
- stream
260
+ generateObject,
261
+ stream,
262
+ streamObject
132
263
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@core-ai/core-ai",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Type-safe LLM abstraction layer over native provider SDKs",
5
5
  "license": "MIT",
6
6
  "author": "Omnifact (https://omnifact.ai)",