@core-ai/anthropic 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Omnifact (https://omnifact.ai)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @core-ai/anthropic
2
+
3
+ Anthropic provider package for `@core-ai/core-ai`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @core-ai/core-ai @core-ai/anthropic zod
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { generate } from '@core-ai/core-ai';
15
+ import { createAnthropic } from '@core-ai/anthropic';
16
+
17
+ const anthropic = createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
18
+ const model = anthropic.chatModel('claude-sonnet-4-20250514');
19
+
20
+ const result = await generate({
21
+ model,
22
+ messages: [{ role: 'user', content: 'Hello!' }],
23
+ });
24
+
25
+ console.log(result.content);
26
+ ```
@@ -0,0 +1,15 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import { ChatModel } from '@core-ai/core-ai';
3
+
4
+ type AnthropicProviderOptions = {
5
+ apiKey?: string;
6
+ baseURL?: string;
7
+ client?: Anthropic;
8
+ defaultMaxTokens?: number;
9
+ };
10
+ type AnthropicProvider = {
11
+ chatModel(modelId: string): ChatModel;
12
+ };
13
+ declare function createAnthropic(options?: AnthropicProviderOptions): AnthropicProvider;
14
+
15
+ export { type AnthropicProvider, type AnthropicProviderOptions, createAnthropic };
package/dist/index.js ADDED
@@ -0,0 +1,383 @@
1
+ // src/provider.ts
2
+ import Anthropic from "@anthropic-ai/sdk";
3
+
4
+ // src/chat-model.ts
5
+ import { createStreamResult } from "@core-ai/core-ai";
6
+
7
+ // src/chat-adapter.ts
8
+ import { APIError } from "@anthropic-ai/sdk";
9
+ import { zodToJsonSchema } from "zod-to-json-schema";
10
+ import { ProviderError } from "@core-ai/core-ai";
11
+ function convertMessages(messages) {
12
+ const systemParts = [];
13
+ const convertedMessages = [];
14
+ let previousInputWasTool = false;
15
+ for (const message of messages) {
16
+ if (message.role === "system") {
17
+ systemParts.push(message.content);
18
+ previousInputWasTool = false;
19
+ continue;
20
+ }
21
+ if (message.role === "user") {
22
+ convertedMessages.push({
23
+ role: "user",
24
+ content: typeof message.content === "string" ? message.content : message.content.map(convertUserContentPart)
25
+ });
26
+ previousInputWasTool = false;
27
+ continue;
28
+ }
29
+ if (message.role === "assistant") {
30
+ const contentBlocks = [];
31
+ if (message.content) {
32
+ contentBlocks.push({
33
+ type: "text",
34
+ text: message.content
35
+ });
36
+ }
37
+ for (const toolCall of message.toolCalls ?? []) {
38
+ contentBlocks.push({
39
+ type: "tool_use",
40
+ id: toolCall.id,
41
+ name: toolCall.name,
42
+ input: toolCall.arguments
43
+ });
44
+ }
45
+ convertedMessages.push({
46
+ role: "assistant",
47
+ content: contentBlocks.length === 0 ? "" : contentBlocks.length === 1 && contentBlocks[0]?.type === "text" ? contentBlocks[0].text : contentBlocks
48
+ });
49
+ previousInputWasTool = false;
50
+ continue;
51
+ }
52
+ const toolResultBlock = {
53
+ type: "tool_result",
54
+ tool_use_id: message.toolCallId,
55
+ content: message.content,
56
+ ...message.isError ? { is_error: true } : {}
57
+ };
58
+ if (previousInputWasTool && convertedMessages.at(-1)?.role === "user" && Array.isArray(convertedMessages.at(-1)?.content)) {
59
+ const lastMessage = convertedMessages.at(-1);
60
+ if (lastMessage && Array.isArray(lastMessage.content)) {
61
+ lastMessage.content.push(toolResultBlock);
62
+ }
63
+ } else {
64
+ convertedMessages.push({
65
+ role: "user",
66
+ content: [toolResultBlock]
67
+ });
68
+ }
69
+ previousInputWasTool = true;
70
+ }
71
+ return {
72
+ system: systemParts.length > 0 ? systemParts.join("\n") : void 0,
73
+ messages: convertedMessages
74
+ };
75
+ }
76
+ function convertUserContentPart(part) {
77
+ if (part.type === "text") {
78
+ return {
79
+ type: "text",
80
+ text: part.text
81
+ };
82
+ }
83
+ if (part.type === "image") {
84
+ if (part.source.type === "url") {
85
+ return {
86
+ type: "image",
87
+ source: {
88
+ type: "url",
89
+ url: part.source.url
90
+ }
91
+ };
92
+ }
93
+ return {
94
+ type: "image",
95
+ source: {
96
+ type: "base64",
97
+ media_type: part.source.mediaType,
98
+ data: part.source.data
99
+ }
100
+ };
101
+ }
102
+ if (part.mimeType !== "application/pdf") {
103
+ throw new Error(
104
+ "Anthropic only supports PDF file content in this abstraction"
105
+ );
106
+ }
107
+ return {
108
+ type: "document",
109
+ source: {
110
+ type: "base64",
111
+ media_type: "application/pdf",
112
+ data: part.data
113
+ }
114
+ };
115
+ }
116
+ function convertTools(tools) {
117
+ return Object.values(tools).map((tool) => {
118
+ const schema = zodToJsonSchema(tool.parameters);
119
+ const { $schema: _schema, ...inputSchema } = schema;
120
+ return {
121
+ name: tool.name,
122
+ description: tool.description,
123
+ input_schema: inputSchema
124
+ };
125
+ });
126
+ }
127
+ function convertToolChoice(choice) {
128
+ if (choice === "auto") {
129
+ return { type: "auto" };
130
+ }
131
+ if (choice === "none") {
132
+ return { type: "none" };
133
+ }
134
+ if (choice === "required") {
135
+ return { type: "any" };
136
+ }
137
+ return {
138
+ type: "tool",
139
+ name: choice.toolName
140
+ };
141
+ }
142
+ function createGenerateRequest(modelId, defaultMaxTokens, options) {
143
+ const converted = convertMessages(options.messages);
144
+ 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 } : {},
154
+ ...options.providerOptions
155
+ };
156
+ }
157
+ function createStreamRequest(modelId, defaultMaxTokens, options) {
158
+ const converted = convertMessages(options.messages);
159
+ return {
160
+ model: modelId,
161
+ messages: converted.messages,
162
+ stream: true,
163
+ max_tokens: options.config?.maxTokens ?? defaultMaxTokens,
164
+ ...converted.system ? { system: converted.system } : {},
165
+ ...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertTools(options.tools) } : {},
166
+ ...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
171
+ };
172
+ }
173
+ function mapGenerateResponse(response) {
174
+ const toolCalls = [];
175
+ let content = "";
176
+ for (const block of response.content) {
177
+ if (block.type === "text") {
178
+ content += block.text;
179
+ continue;
180
+ }
181
+ if (block.type === "tool_use") {
182
+ toolCalls.push({
183
+ id: block.id,
184
+ name: block.name,
185
+ arguments: asObject(block.input)
186
+ });
187
+ }
188
+ }
189
+ return {
190
+ content: content.length > 0 ? content : null,
191
+ toolCalls,
192
+ finishReason: mapStopReason(response.stop_reason),
193
+ usage: {
194
+ inputTokens: response.usage.input_tokens,
195
+ outputTokens: response.usage.output_tokens,
196
+ reasoningTokens: 0,
197
+ totalTokens: response.usage.input_tokens + response.usage.output_tokens
198
+ }
199
+ };
200
+ }
201
+ async function* transformStream(stream) {
202
+ let finishReason = "unknown";
203
+ let usage = {
204
+ inputTokens: 0,
205
+ outputTokens: 0,
206
+ reasoningTokens: 0,
207
+ totalTokens: 0
208
+ };
209
+ const toolBuffers = /* @__PURE__ */ new Map();
210
+ const emittedToolCalls = /* @__PURE__ */ new Set();
211
+ for await (const event of stream) {
212
+ if (event.type === "message_start") {
213
+ usage = {
214
+ inputTokens: event.message.usage.input_tokens,
215
+ outputTokens: event.message.usage.output_tokens,
216
+ reasoningTokens: 0,
217
+ totalTokens: event.message.usage.input_tokens + event.message.usage.output_tokens
218
+ };
219
+ continue;
220
+ }
221
+ if (event.type === "content_block_start") {
222
+ if (event.content_block.type === "tool_use") {
223
+ const block = event.content_block;
224
+ const initialArguments = block.input && typeof block.input === "object" ? JSON.stringify(block.input) : "";
225
+ toolBuffers.set(event.index, {
226
+ id: block.id,
227
+ name: block.name,
228
+ arguments: initialArguments
229
+ });
230
+ yield {
231
+ type: "tool-call-start",
232
+ toolCallId: block.id,
233
+ toolName: block.name
234
+ };
235
+ }
236
+ continue;
237
+ }
238
+ if (event.type === "content_block_delta") {
239
+ if (event.delta.type === "text_delta") {
240
+ yield {
241
+ type: "content-delta",
242
+ text: event.delta.text
243
+ };
244
+ continue;
245
+ }
246
+ if (event.delta.type === "input_json_delta") {
247
+ const current = toolBuffers.get(event.index);
248
+ if (!current) {
249
+ continue;
250
+ }
251
+ current.arguments += event.delta.partial_json;
252
+ yield {
253
+ type: "tool-call-delta",
254
+ toolCallId: current.id,
255
+ argumentsDelta: event.delta.partial_json
256
+ };
257
+ }
258
+ continue;
259
+ }
260
+ if (event.type === "content_block_stop") {
261
+ const current = toolBuffers.get(event.index);
262
+ if (!current || emittedToolCalls.has(event.index)) {
263
+ continue;
264
+ }
265
+ emittedToolCalls.add(event.index);
266
+ yield {
267
+ type: "tool-call-end",
268
+ toolCall: {
269
+ id: current.id,
270
+ name: current.name,
271
+ arguments: safeParseJsonObject(current.arguments)
272
+ }
273
+ };
274
+ continue;
275
+ }
276
+ if (event.type === "message_delta") {
277
+ finishReason = mapStopReason(event.delta.stop_reason);
278
+ usage = {
279
+ inputTokens: event.usage.input_tokens ?? usage.inputTokens,
280
+ outputTokens: event.usage.output_tokens,
281
+ reasoningTokens: 0,
282
+ totalTokens: (event.usage.input_tokens ?? usage.inputTokens) + event.usage.output_tokens
283
+ };
284
+ continue;
285
+ }
286
+ }
287
+ yield {
288
+ type: "finish",
289
+ finishReason,
290
+ usage
291
+ };
292
+ }
293
+ function mapStopReason(reason) {
294
+ if (reason === "end_turn" || reason === "stop_sequence") {
295
+ return "stop";
296
+ }
297
+ if (reason === "max_tokens") {
298
+ return "length";
299
+ }
300
+ if (reason === "tool_use") {
301
+ return "tool-calls";
302
+ }
303
+ if (reason === "refusal") {
304
+ return "content-filter";
305
+ }
306
+ return "unknown";
307
+ }
308
+ function safeParseJsonObject(json) {
309
+ try {
310
+ const parsed = JSON.parse(json);
311
+ return asObject(parsed);
312
+ } catch {
313
+ return {};
314
+ }
315
+ }
316
+ function asObject(value) {
317
+ if (value && typeof value === "object" && !Array.isArray(value)) {
318
+ return value;
319
+ }
320
+ return {};
321
+ }
322
+ function wrapError(error) {
323
+ if (error instanceof APIError) {
324
+ return new ProviderError(error.message, "anthropic", error.status, error);
325
+ }
326
+ return new ProviderError(
327
+ error instanceof Error ? error.message : String(error),
328
+ "anthropic",
329
+ void 0,
330
+ error
331
+ );
332
+ }
333
+
334
+ // src/chat-model.ts
335
+ function createAnthropicChatModel(client, modelId, defaultMaxTokens) {
336
+ return {
337
+ provider: "anthropic",
338
+ 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
+ }
351
+ },
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
+ }
366
+ }
367
+ };
368
+ }
369
+
370
+ // src/provider.ts
371
+ function createAnthropic(options = {}) {
372
+ const client = options.client ?? new Anthropic({
373
+ apiKey: options.apiKey,
374
+ baseURL: options.baseURL
375
+ });
376
+ const defaultMaxTokens = options.defaultMaxTokens ?? 4096;
377
+ return {
378
+ chatModel: (modelId) => createAnthropicChatModel(client, modelId, defaultMaxTokens)
379
+ };
380
+ }
381
+ export {
382
+ createAnthropic
383
+ };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@core-ai/anthropic",
3
+ "version": "0.1.0",
4
+ "description": "Anthropic provider package for @core-ai/core-ai",
5
+ "license": "MIT",
6
+ "author": "Omnifact (https://omnifact.ai)",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/agdevhq/ai-core.git",
10
+ "directory": "packages/anthropic"
11
+ },
12
+ "keywords": ["llm", "ai", "anthropic", "provider", "sdk"],
13
+ "type": "module",
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ }
21
+ },
22
+ "files": ["dist", "README.md", "LICENSE"],
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "scripts": {
27
+ "build": "tsup",
28
+ "lint": "eslint src/ --max-warnings 0",
29
+ "check-types": "tsc --noEmit",
30
+ "prepublishOnly": "npm run build",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest"
33
+ },
34
+ "dependencies": {
35
+ "@core-ai/core-ai": "^0.1.0",
36
+ "@anthropic-ai/sdk": "^0.78.0",
37
+ "zod-to-json-schema": "^3.24.5"
38
+ },
39
+ "peerDependencies": {
40
+ "zod": "^3.25.76"
41
+ },
42
+ "devDependencies": {
43
+ "@core-ai/eslint-config": "^0.0.0",
44
+ "@core-ai/typescript-config": "^0.0.0",
45
+ "typescript": "^5.7.3",
46
+ "vitest": "^3.2.4"
47
+ }
48
+ }