@core-ai/anthropic 0.2.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +3 -1
  2. package/dist/index.js +251 -48
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @core-ai/anthropic
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@core-ai/anthropic.svg)](https://www.npmjs.com/package/@core-ai/anthropic)
4
+
3
5
  Anthropic provider package for `@core-ai/core-ai`.
4
6
 
5
7
  ## Installation
@@ -15,7 +17,7 @@ import { generate } from '@core-ai/core-ai';
15
17
  import { createAnthropic } from '@core-ai/anthropic';
16
18
 
17
19
  const anthropic = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
18
- const model = anthropic.chatModel('claude-sonnet-4-20250514');
20
+ const model = anthropic.chatModel('claude-haiku-4-5');
19
21
 
20
22
  const result = await generate({
21
23
  model,
package/dist/index.js CHANGED
@@ -2,12 +2,28 @@
2
2
  import Anthropic from "@anthropic-ai/sdk";
3
3
 
4
4
  // src/chat-model.ts
5
- import { createStreamResult } from "@core-ai/core-ai";
5
+ import {
6
+ StructuredOutputNoObjectGeneratedError,
7
+ StructuredOutputParseError,
8
+ StructuredOutputValidationError,
9
+ createObjectStreamResult,
10
+ createStreamResult
11
+ } from "@core-ai/core-ai";
6
12
 
7
13
  // src/chat-adapter.ts
8
14
  import { APIError } from "@anthropic-ai/sdk";
9
15
  import { zodToJsonSchema } from "zod-to-json-schema";
10
16
  import { ProviderError } from "@core-ai/core-ai";
17
+ var UNSUPPORTED_ANTHROPIC_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
18
+ "minimum",
19
+ "maximum",
20
+ "exclusiveMinimum",
21
+ "exclusiveMaximum",
22
+ "multipleOf",
23
+ "minLength",
24
+ "maxLength",
25
+ "maxItems"
26
+ ]);
11
27
  function convertMessages(messages) {
12
28
  const systemParts = [];
13
29
  const convertedMessages = [];
@@ -115,12 +131,12 @@ function convertUserContentPart(part) {
115
131
  }
116
132
  function convertTools(tools) {
117
133
  return Object.values(tools).map((tool) => {
118
- const schema = zodToJsonSchema(tool.parameters);
119
- const { $schema: _schema, ...inputSchema } = schema;
134
+ const schema = toAnthropicJsonSchema(tool.parameters);
120
135
  return {
121
136
  name: tool.name,
122
137
  description: tool.description,
123
- input_schema: inputSchema
138
+ input_schema: schema,
139
+ strict: true
124
140
  };
125
141
  });
126
142
  }
@@ -139,35 +155,90 @@ function convertToolChoice(choice) {
139
155
  name: choice.toolName
140
156
  };
141
157
  }
158
+ function createStructuredOutputOptions(options) {
159
+ const schema = toAnthropicJsonSchema(options.schema);
160
+ const schemaDescription = options.schemaDescription?.trim();
161
+ if (schemaDescription && schemaDescription.length > 0) {
162
+ schema.description = schemaDescription;
163
+ }
164
+ return {
165
+ messages: options.messages,
166
+ config: options.config,
167
+ providerOptions: {
168
+ ...options.providerOptions ?? {},
169
+ output_config: {
170
+ format: {
171
+ type: "json_schema",
172
+ schema
173
+ }
174
+ }
175
+ },
176
+ signal: options.signal
177
+ };
178
+ }
179
+ function toAnthropicJsonSchema(schema) {
180
+ const rawSchema = zodToJsonSchema(schema);
181
+ return normalizeAnthropicJsonSchema(rawSchema);
182
+ }
183
+ function normalizeAnthropicJsonSchema(value) {
184
+ const normalized = normalizeAnthropicJsonValue(value);
185
+ return isJsonObject(normalized) ? normalized : {};
186
+ }
187
+ function normalizeAnthropicJsonValue(value) {
188
+ if (Array.isArray(value)) {
189
+ return value.map(normalizeAnthropicJsonValue);
190
+ }
191
+ if (!isJsonObject(value)) {
192
+ return value;
193
+ }
194
+ const normalized = {};
195
+ for (const [key, child] of Object.entries(value)) {
196
+ if (key === "$schema" || UNSUPPORTED_ANTHROPIC_SCHEMA_KEYWORDS.has(key) || key === "minItems" && typeof child === "number" && child > 1) {
197
+ continue;
198
+ }
199
+ normalized[key] = normalizeAnthropicJsonValue(child);
200
+ }
201
+ if (isObjectSchema(normalized)) {
202
+ normalized.additionalProperties = false;
203
+ }
204
+ return normalized;
205
+ }
206
+ function isObjectSchema(value) {
207
+ return value.type === "object" || Object.hasOwn(value, "properties") || Object.hasOwn(value, "required");
208
+ }
209
+ function isJsonObject(value) {
210
+ return value !== null && typeof value === "object" && !Array.isArray(value);
211
+ }
142
212
  function createGenerateRequest(modelId, defaultMaxTokens, options) {
143
- const converted = convertMessages(options.messages);
144
213
  return {
145
- model: modelId,
146
- messages: converted.messages,
147
- max_tokens: options.config?.maxTokens ?? defaultMaxTokens,
148
- ...converted.system ? { system: converted.system } : {},
149
- ...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertTools(options.tools) } : {},
150
- ...options.toolChoice ? { tool_choice: convertToolChoice(options.toolChoice) } : {},
151
- ...options.config?.temperature !== void 0 ? { temperature: options.config.temperature } : {},
152
- ...options.config?.topP !== void 0 ? { top_p: options.config.topP } : {},
153
- ...options.config?.stopSequences ? { stop_sequences: options.config.stopSequences } : {},
214
+ ...createRequestBase(modelId, defaultMaxTokens, options),
154
215
  ...options.providerOptions
155
216
  };
156
217
  }
157
218
  function createStreamRequest(modelId, defaultMaxTokens, options) {
219
+ return {
220
+ ...createRequestBase(modelId, defaultMaxTokens, options),
221
+ stream: true,
222
+ ...options.providerOptions
223
+ };
224
+ }
225
+ function createRequestBase(modelId, defaultMaxTokens, options) {
158
226
  const converted = convertMessages(options.messages);
159
227
  return {
160
228
  model: modelId,
161
229
  messages: converted.messages,
162
- stream: true,
163
230
  max_tokens: options.config?.maxTokens ?? defaultMaxTokens,
164
231
  ...converted.system ? { system: converted.system } : {},
165
232
  ...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertTools(options.tools) } : {},
166
233
  ...options.toolChoice ? { tool_choice: convertToolChoice(options.toolChoice) } : {},
167
- ...options.config?.temperature !== void 0 ? { temperature: options.config.temperature } : {},
168
- ...options.config?.topP !== void 0 ? { top_p: options.config.topP } : {},
169
- ...options.config?.stopSequences ? { stop_sequences: options.config.stopSequences } : {},
170
- ...options.providerOptions
234
+ ...mapConfigToRequestFields(options.config)
235
+ };
236
+ }
237
+ function mapConfigToRequestFields(config) {
238
+ return {
239
+ ...config?.temperature !== void 0 ? { temperature: config.temperature } : {},
240
+ ...config?.topP !== void 0 ? { top_p: config.topP } : {},
241
+ ...config?.stopSequences ? { stop_sequences: config.stopSequences } : {}
171
242
  };
172
243
  }
173
244
  function mapGenerateResponse(response) {
@@ -221,7 +292,7 @@ async function* transformStream(stream) {
221
292
  if (event.type === "content_block_start") {
222
293
  if (event.content_block.type === "tool_use") {
223
294
  const block = event.content_block;
224
- const initialArguments = block.input && typeof block.input === "object" ? JSON.stringify(block.input) : "";
295
+ const initialArguments = block.input && typeof block.input === "object" ? Object.keys(block.input).length > 0 ? JSON.stringify(block.input) : "" : "";
225
296
  toolBuffers.set(event.index, {
226
297
  id: block.id,
227
298
  name: block.name,
@@ -321,7 +392,12 @@ function asObject(value) {
321
392
  }
322
393
  function wrapError(error) {
323
394
  if (error instanceof APIError) {
324
- return new ProviderError(error.message, "anthropic", error.status, error);
395
+ return new ProviderError(
396
+ error.message,
397
+ "anthropic",
398
+ error.status,
399
+ error
400
+ );
325
401
  }
326
402
  return new ProviderError(
327
403
  error instanceof Error ? error.message : String(error),
@@ -333,39 +409,166 @@ function wrapError(error) {
333
409
 
334
410
  // src/chat-model.ts
335
411
  function createAnthropicChatModel(client, modelId, defaultMaxTokens) {
412
+ const provider = "anthropic";
413
+ async function callAnthropicMessagesApi(request) {
414
+ try {
415
+ return await client.messages.create(request);
416
+ } catch (error) {
417
+ throw wrapError(error);
418
+ }
419
+ }
420
+ async function generateChat(options) {
421
+ const request = createGenerateRequest(modelId, defaultMaxTokens, options);
422
+ const response = await callAnthropicMessagesApi(request);
423
+ return mapGenerateResponse(response);
424
+ }
425
+ async function streamChat(options) {
426
+ const request = createStreamRequest(modelId, defaultMaxTokens, options);
427
+ const stream = await callAnthropicMessagesApi(request);
428
+ return createStreamResult(transformStream(stream));
429
+ }
336
430
  return {
337
- provider: "anthropic",
431
+ provider,
338
432
  modelId,
339
- async generate(options) {
340
- try {
341
- const request = createGenerateRequest(
342
- modelId,
343
- defaultMaxTokens,
344
- options
345
- );
346
- const response = await client.messages.create(request);
347
- return mapGenerateResponse(response);
348
- } catch (error) {
349
- throw wrapError(error);
350
- }
433
+ generate: generateChat,
434
+ stream: streamChat,
435
+ async generateObject(options) {
436
+ const structuredOptions = createStructuredOutputOptions(options);
437
+ const result = await generateChat(structuredOptions);
438
+ const object = extractStructuredObject(result, options.schema, provider);
439
+ return {
440
+ object,
441
+ finishReason: result.finishReason,
442
+ usage: result.usage
443
+ };
351
444
  },
352
- async stream(options) {
353
- try {
354
- const request = createStreamRequest(
355
- modelId,
356
- defaultMaxTokens,
357
- options
358
- );
359
- const stream = await client.messages.create(
360
- request
361
- );
362
- return createStreamResult(transformStream(stream));
363
- } catch (error) {
364
- throw wrapError(error);
365
- }
445
+ async streamObject(options) {
446
+ const structuredOptions = createStructuredOutputOptions(options);
447
+ const stream = await streamChat(structuredOptions);
448
+ return createObjectStreamResult(
449
+ transformStructuredOutputStream(
450
+ stream,
451
+ options.schema,
452
+ provider
453
+ )
454
+ );
366
455
  }
367
456
  };
368
457
  }
458
+ function extractStructuredObject(result, schema, provider) {
459
+ const rawOutput = requireStructuredOutputPayload(
460
+ result.finishReason,
461
+ result.content?.trim(),
462
+ provider,
463
+ "model did not emit a structured object payload"
464
+ );
465
+ return parseAndValidateStructuredObject(schema, rawOutput, provider);
466
+ }
467
+ async function* transformStructuredOutputStream(stream, schema, provider) {
468
+ let contentBuffer = "";
469
+ for await (const event of stream) {
470
+ if (event.type === "content-delta") {
471
+ contentBuffer += event.text;
472
+ yield {
473
+ type: "object-delta",
474
+ text: event.text
475
+ };
476
+ continue;
477
+ }
478
+ if (event.type === "tool-call-delta") {
479
+ contentBuffer += event.argumentsDelta;
480
+ yield {
481
+ type: "object-delta",
482
+ text: event.argumentsDelta
483
+ };
484
+ continue;
485
+ }
486
+ if (event.type === "finish") {
487
+ const rawOutput = requireStructuredOutputPayload(
488
+ event.finishReason,
489
+ contentBuffer.trim(),
490
+ provider,
491
+ "structured output stream ended without an object payload"
492
+ );
493
+ const validatedObject = parseAndValidateStructuredObject(
494
+ schema,
495
+ rawOutput,
496
+ provider
497
+ );
498
+ yield {
499
+ type: "object",
500
+ object: validatedObject
501
+ };
502
+ yield {
503
+ type: "finish",
504
+ finishReason: event.finishReason,
505
+ usage: event.usage
506
+ };
507
+ }
508
+ }
509
+ }
510
+ function requireStructuredOutputPayload(finishReason, rawOutput, provider, noPayloadMessage) {
511
+ if (finishReason === "content-filter") {
512
+ throw new StructuredOutputNoObjectGeneratedError(
513
+ "model refused to produce a structured output",
514
+ provider,
515
+ {
516
+ rawOutput
517
+ }
518
+ );
519
+ }
520
+ if (finishReason === "length") {
521
+ throw new StructuredOutputNoObjectGeneratedError(
522
+ "structured output was truncated because max tokens were reached",
523
+ provider,
524
+ {
525
+ rawOutput
526
+ }
527
+ );
528
+ }
529
+ if (!rawOutput || rawOutput.length === 0) {
530
+ throw new StructuredOutputNoObjectGeneratedError(noPayloadMessage, provider);
531
+ }
532
+ return rawOutput;
533
+ }
534
+ function parseAndValidateStructuredObject(schema, rawOutput, provider) {
535
+ const parsedOutput = parseJson(rawOutput, provider);
536
+ return validateStructuredObject(schema, parsedOutput, provider, rawOutput);
537
+ }
538
+ function parseJson(rawOutput, provider) {
539
+ try {
540
+ return JSON.parse(rawOutput);
541
+ } catch (error) {
542
+ throw new StructuredOutputParseError(
543
+ "failed to parse structured output as JSON",
544
+ provider,
545
+ {
546
+ rawOutput,
547
+ cause: error
548
+ }
549
+ );
550
+ }
551
+ }
552
+ function validateStructuredObject(schema, value, provider, rawOutput) {
553
+ const parsed = schema.safeParse(value);
554
+ if (parsed.success) {
555
+ return parsed.data;
556
+ }
557
+ throw new StructuredOutputValidationError(
558
+ "structured output does not match schema",
559
+ provider,
560
+ formatZodIssues(parsed.error.issues),
561
+ {
562
+ rawOutput
563
+ }
564
+ );
565
+ }
566
+ function formatZodIssues(issues) {
567
+ return issues.map((issue) => {
568
+ const path = issue.path.length > 0 ? issue.path.map((segment) => String(segment)).join(".") : "<root>";
569
+ return `${path}: ${issue.message}`;
570
+ });
571
+ }
369
572
 
370
573
  // src/provider.ts
371
574
  function createAnthropic(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@core-ai/anthropic",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Anthropic provider package for @core-ai/core-ai",
5
5
  "license": "MIT",
6
6
  "author": "Omnifact (https://omnifact.ai)",
@@ -43,12 +43,12 @@
43
43
  "test:watch": "vitest"
44
44
  },
45
45
  "dependencies": {
46
- "@core-ai/core-ai": "^0.2.0",
46
+ "@core-ai/core-ai": "^0.3.0",
47
47
  "@anthropic-ai/sdk": "^0.78.0",
48
- "zod-to-json-schema": "^3.24.5"
48
+ "zod-to-json-schema": "^3.25.1"
49
49
  },
50
50
  "peerDependencies": {
51
- "zod": "^3.25.76"
51
+ "zod": "^3.25.0 || ^4.0.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@core-ai/eslint-config": "^0.0.0",