@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.
- package/README.md +3 -1
- package/dist/index.js +251 -48
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @core-ai/anthropic
|
|
2
2
|
|
|
3
|
+
[](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-
|
|
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 {
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
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
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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(
|
|
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
|
|
431
|
+
provider,
|
|
338
432
|
modelId,
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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.
|
|
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.
|
|
46
|
+
"@core-ai/core-ai": "^0.3.0",
|
|
47
47
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
48
|
-
"zod-to-json-schema": "^3.
|
|
48
|
+
"zod-to-json-schema": "^3.25.1"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"zod": "^3.25.
|
|
51
|
+
"zod": "^3.25.0 || ^4.0.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@core-ai/eslint-config": "^0.0.0",
|