@bernierllc/ai-provider-anthropic 1.0.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/.eslintrc.js +34 -0
- package/README.md +310 -0
- package/__tests__/AnthropicProvider.test.ts +655 -0
- package/__tests__/error-handling.test.ts +208 -0
- package/__tests__/message-conversion.test.ts +161 -0
- package/__tests__/model-registry.test.ts +150 -0
- package/dist/AnthropicProvider.d.ts +54 -0
- package/dist/AnthropicProvider.js +337 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +34 -0
- package/dist/models/model-registry.d.ts +31 -0
- package/dist/models/model-registry.js +113 -0
- package/dist/types/anthropic-types.d.ts +46 -0
- package/dist/types/anthropic-types.js +9 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +24 -0
- package/dist/utils/error-handling.d.ts +12 -0
- package/dist/utils/error-handling.js +67 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +25 -0
- package/dist/utils/message-conversion.d.ts +17 -0
- package/dist/utils/message-conversion.js +65 -0
- package/jest.config.js +30 -0
- package/package.json +59 -0
- package/src/AnthropicProvider.ts +392 -0
- package/src/index.ts +19 -0
- package/src/models/model-registry.ts +120 -0
- package/src/types/anthropic-types.ts +60 -0
- package/src/types/index.ts +9 -0
- package/src/utils/error-handling.ts +71 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/message-conversion.ts +78 -0
- package/tsconfig.json +29 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code only within the scope of the project it was delivered for.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.AnthropicProvider = void 0;
|
|
14
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
15
|
+
const ai_provider_core_1 = require("@bernierllc/ai-provider-core");
|
|
16
|
+
const model_registry_1 = require("./models/model-registry");
|
|
17
|
+
const message_conversion_1 = require("./utils/message-conversion");
|
|
18
|
+
const error_handling_1 = require("./utils/error-handling");
|
|
19
|
+
/**
|
|
20
|
+
* Anthropic Provider Implementation
|
|
21
|
+
* Concrete implementation of the AI provider interface for Anthropic's Claude API
|
|
22
|
+
*/
|
|
23
|
+
class AnthropicProvider extends ai_provider_core_1.AIProvider {
|
|
24
|
+
constructor(config) {
|
|
25
|
+
super(config);
|
|
26
|
+
this.client = new sdk_1.default({
|
|
27
|
+
apiKey: config.apiKey,
|
|
28
|
+
baseURL: config.baseURL,
|
|
29
|
+
timeout: config.timeout || 60000,
|
|
30
|
+
maxRetries: config.maxRetries || 3
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// ============================================
|
|
34
|
+
// CORE OPERATIONS
|
|
35
|
+
// ============================================
|
|
36
|
+
/**
|
|
37
|
+
* Generate text completion using Anthropic Claude API
|
|
38
|
+
*/
|
|
39
|
+
async complete(request) {
|
|
40
|
+
// Validate request
|
|
41
|
+
const validation = this.validateRequest(request);
|
|
42
|
+
if (!validation.isValid) {
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
error: validation.errors.join(', ')
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// Validate messages
|
|
49
|
+
const messageValidation = (0, message_conversion_1.validateMessages)(request.messages);
|
|
50
|
+
if (!messageValidation.isValid) {
|
|
51
|
+
return {
|
|
52
|
+
success: false,
|
|
53
|
+
error: messageValidation.error
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
// Convert messages to Anthropic format
|
|
58
|
+
const { system, messages } = (0, message_conversion_1.convertMessagesToAnthropicFormat)(request.messages);
|
|
59
|
+
const completion = await this.client.messages.create({
|
|
60
|
+
model: request.model || this.config.defaultModel || 'claude-3-opus-20240229',
|
|
61
|
+
system,
|
|
62
|
+
messages,
|
|
63
|
+
max_tokens: request.maxTokens || 4096,
|
|
64
|
+
temperature: request.temperature,
|
|
65
|
+
top_p: request.topP,
|
|
66
|
+
stop_sequences: request.stop,
|
|
67
|
+
metadata: request.user ? {
|
|
68
|
+
user_id: request.user
|
|
69
|
+
} : undefined
|
|
70
|
+
});
|
|
71
|
+
// Extract content from response
|
|
72
|
+
const content = completion.content
|
|
73
|
+
.filter((block) => block.type === 'text')
|
|
74
|
+
.map(block => block.text)
|
|
75
|
+
.join('');
|
|
76
|
+
return {
|
|
77
|
+
success: true,
|
|
78
|
+
content,
|
|
79
|
+
finishReason: this.mapStopReason(completion.stop_reason),
|
|
80
|
+
usage: {
|
|
81
|
+
promptTokens: completion.usage.input_tokens,
|
|
82
|
+
completionTokens: completion.usage.output_tokens,
|
|
83
|
+
totalTokens: completion.usage.input_tokens + completion.usage.output_tokens
|
|
84
|
+
},
|
|
85
|
+
model: completion.model,
|
|
86
|
+
metadata: {
|
|
87
|
+
id: completion.id,
|
|
88
|
+
type: completion.type,
|
|
89
|
+
role: completion.role,
|
|
90
|
+
stopSequence: completion.stop_sequence
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
const aiError = (0, error_handling_1.handleAnthropicError)(error);
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
error: aiError.message
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Generate streaming text completion
|
|
104
|
+
*/
|
|
105
|
+
async *streamComplete(request) {
|
|
106
|
+
const validation = this.validateRequest(request);
|
|
107
|
+
if (!validation.isValid) {
|
|
108
|
+
throw new Error(validation.errors.join(', '));
|
|
109
|
+
}
|
|
110
|
+
const messageValidation = (0, message_conversion_1.validateMessages)(request.messages);
|
|
111
|
+
if (!messageValidation.isValid) {
|
|
112
|
+
throw new Error(messageValidation.error || 'Invalid messages');
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const { system, messages } = (0, message_conversion_1.convertMessagesToAnthropicFormat)(request.messages);
|
|
116
|
+
const stream = this.client.messages.stream({
|
|
117
|
+
model: request.model || this.config.defaultModel || 'claude-3-opus-20240229',
|
|
118
|
+
system,
|
|
119
|
+
messages,
|
|
120
|
+
max_tokens: request.maxTokens || 4096,
|
|
121
|
+
temperature: request.temperature,
|
|
122
|
+
top_p: request.topP,
|
|
123
|
+
stop_sequences: request.stop
|
|
124
|
+
});
|
|
125
|
+
let finalUsage;
|
|
126
|
+
let finalStopReason;
|
|
127
|
+
for await (const event of stream) {
|
|
128
|
+
if (event.type === 'content_block_delta') {
|
|
129
|
+
const delta = event.delta;
|
|
130
|
+
if (delta.type === 'text_delta') {
|
|
131
|
+
yield {
|
|
132
|
+
delta: delta.text,
|
|
133
|
+
finishReason: undefined
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (event.type === 'message_delta') {
|
|
138
|
+
// Capture usage information from message_delta event
|
|
139
|
+
const usage = event.usage;
|
|
140
|
+
if (usage) {
|
|
141
|
+
finalUsage = {
|
|
142
|
+
promptTokens: usage.input_tokens,
|
|
143
|
+
completionTokens: usage.output_tokens,
|
|
144
|
+
totalTokens: usage.input_tokens + usage.output_tokens
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (event.type === 'message_stop') {
|
|
149
|
+
// Capture stop reason
|
|
150
|
+
finalStopReason = null; // message_stop doesn't provide stop_reason directly
|
|
151
|
+
yield {
|
|
152
|
+
delta: '',
|
|
153
|
+
finishReason: this.mapStopReason(finalStopReason),
|
|
154
|
+
usage: finalUsage
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
throw (0, error_handling_1.handleAnthropicError)(error);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Generate embeddings (Anthropic doesn't provide embeddings API)
|
|
165
|
+
* Returns error indicating feature not supported
|
|
166
|
+
*/
|
|
167
|
+
generateEmbeddings(_request) {
|
|
168
|
+
return Promise.resolve({
|
|
169
|
+
success: false,
|
|
170
|
+
error: 'Anthropic does not provide an embeddings API. Use OpenAI provider for embeddings.'
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Check content moderation (Anthropic doesn't provide moderation API)
|
|
175
|
+
* Returns success with no flags (Claude has built-in safety)
|
|
176
|
+
*/
|
|
177
|
+
moderate(_content) {
|
|
178
|
+
// Claude has built-in safety features
|
|
179
|
+
// We can return success with no flags as Claude refuses unsafe content
|
|
180
|
+
return Promise.resolve({
|
|
181
|
+
success: true,
|
|
182
|
+
flagged: false,
|
|
183
|
+
categories: {},
|
|
184
|
+
categoryScores: {}
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Get available Anthropic models
|
|
189
|
+
*/
|
|
190
|
+
getAvailableModels() {
|
|
191
|
+
// Anthropic doesn't provide a models list endpoint
|
|
192
|
+
// Return cached model information
|
|
193
|
+
return Promise.resolve(model_registry_1.ClaudeModelRegistry.getAllModels());
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Check Anthropic API health
|
|
197
|
+
*/
|
|
198
|
+
async checkHealth() {
|
|
199
|
+
const startTime = Date.now();
|
|
200
|
+
try {
|
|
201
|
+
// Simple test request with minimal tokens
|
|
202
|
+
await this.client.messages.create({
|
|
203
|
+
model: 'claude-3-haiku-20240307',
|
|
204
|
+
max_tokens: 10,
|
|
205
|
+
messages: [
|
|
206
|
+
{ role: 'user', content: 'Hi' }
|
|
207
|
+
]
|
|
208
|
+
});
|
|
209
|
+
return {
|
|
210
|
+
status: 'healthy',
|
|
211
|
+
latency: Date.now() - startTime,
|
|
212
|
+
lastChecked: new Date()
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
return {
|
|
217
|
+
status: 'unavailable',
|
|
218
|
+
latency: Date.now() - startTime,
|
|
219
|
+
lastChecked: new Date(),
|
|
220
|
+
details: {
|
|
221
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// ============================================
|
|
227
|
+
// ANTHROPIC-SPECIFIC FEATURES
|
|
228
|
+
// ============================================
|
|
229
|
+
/**
|
|
230
|
+
* Extended context completion (200K tokens for all Claude 3 models)
|
|
231
|
+
*/
|
|
232
|
+
async extendedContextCompletion(request) {
|
|
233
|
+
// All Claude 3 models support 200K context window
|
|
234
|
+
const model = request.model || 'claude-3-opus-20240229';
|
|
235
|
+
return this.complete({
|
|
236
|
+
...request,
|
|
237
|
+
model
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Vision analysis (All Claude 3 models support vision)
|
|
242
|
+
*/
|
|
243
|
+
async analyzeImage(imageData, prompt, model = 'claude-3-opus-20240229', maxTokens, temperature) {
|
|
244
|
+
try {
|
|
245
|
+
// Convert image to base64 if needed
|
|
246
|
+
const base64Image = Buffer.isBuffer(imageData)
|
|
247
|
+
? imageData.toString('base64')
|
|
248
|
+
: imageData;
|
|
249
|
+
const completion = await this.client.messages.create({
|
|
250
|
+
model,
|
|
251
|
+
max_tokens: maxTokens || 4096,
|
|
252
|
+
temperature,
|
|
253
|
+
messages: [
|
|
254
|
+
{
|
|
255
|
+
role: 'user',
|
|
256
|
+
content: [
|
|
257
|
+
{
|
|
258
|
+
type: 'image',
|
|
259
|
+
source: {
|
|
260
|
+
type: 'base64',
|
|
261
|
+
media_type: 'image/jpeg',
|
|
262
|
+
data: base64Image
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
type: 'text',
|
|
267
|
+
text: prompt
|
|
268
|
+
}
|
|
269
|
+
]
|
|
270
|
+
}
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
const content = completion.content
|
|
274
|
+
.filter((block) => block.type === 'text')
|
|
275
|
+
.map(block => block.text)
|
|
276
|
+
.join('');
|
|
277
|
+
return {
|
|
278
|
+
success: true,
|
|
279
|
+
content,
|
|
280
|
+
finishReason: this.mapStopReason(completion.stop_reason),
|
|
281
|
+
usage: {
|
|
282
|
+
promptTokens: completion.usage.input_tokens,
|
|
283
|
+
completionTokens: completion.usage.output_tokens,
|
|
284
|
+
totalTokens: completion.usage.input_tokens + completion.usage.output_tokens
|
|
285
|
+
},
|
|
286
|
+
model: completion.model
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
const aiError = (0, error_handling_1.handleAnthropicError)(error);
|
|
291
|
+
return {
|
|
292
|
+
success: false,
|
|
293
|
+
error: aiError.message
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// ============================================
|
|
298
|
+
// COST ESTIMATION (OVERRIDE)
|
|
299
|
+
// ============================================
|
|
300
|
+
/**
|
|
301
|
+
* Estimate cost using Anthropic pricing
|
|
302
|
+
*/
|
|
303
|
+
estimateCost(request) {
|
|
304
|
+
const model = request.model || this.config.defaultModel || 'claude-3-opus-20240229';
|
|
305
|
+
const pricing = model_registry_1.ClaudeModelRegistry.getModelPricing(model);
|
|
306
|
+
const inputTokens = this.estimateTokens(request.messages.map(m => m.content).join(' '));
|
|
307
|
+
const outputTokens = request.maxTokens || 4096;
|
|
308
|
+
const inputCost = (inputTokens / 1000000) * pricing.inputPrice;
|
|
309
|
+
const outputCost = (outputTokens / 1000000) * pricing.outputPrice;
|
|
310
|
+
return {
|
|
311
|
+
inputTokens,
|
|
312
|
+
outputTokens,
|
|
313
|
+
totalTokens: inputTokens + outputTokens,
|
|
314
|
+
estimatedCostUSD: inputCost + outputCost,
|
|
315
|
+
currency: 'USD'
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
// ============================================
|
|
319
|
+
// PRIVATE METHODS
|
|
320
|
+
// ============================================
|
|
321
|
+
/**
|
|
322
|
+
* Map Anthropic stop reason to unified format
|
|
323
|
+
*/
|
|
324
|
+
mapStopReason(stopReason) {
|
|
325
|
+
switch (stopReason) {
|
|
326
|
+
case 'end_turn':
|
|
327
|
+
return 'stop';
|
|
328
|
+
case 'max_tokens':
|
|
329
|
+
return 'length';
|
|
330
|
+
case 'stop_sequence':
|
|
331
|
+
return 'stop';
|
|
332
|
+
default:
|
|
333
|
+
return 'stop';
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
exports.AnthropicProvider = AnthropicProvider;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code only within the scope of the project it was delivered for.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
21
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.ClaudeModelRegistry = exports.AnthropicProvider = void 0;
|
|
25
|
+
// Main provider class
|
|
26
|
+
var AnthropicProvider_1 = require("./AnthropicProvider");
|
|
27
|
+
Object.defineProperty(exports, "AnthropicProvider", { enumerable: true, get: function () { return AnthropicProvider_1.AnthropicProvider; } });
|
|
28
|
+
// Types
|
|
29
|
+
__exportStar(require("./types"), exports);
|
|
30
|
+
// Models
|
|
31
|
+
var model_registry_1 = require("./models/model-registry");
|
|
32
|
+
Object.defineProperty(exports, "ClaudeModelRegistry", { enumerable: true, get: function () { return model_registry_1.ClaudeModelRegistry; } });
|
|
33
|
+
// Utilities
|
|
34
|
+
__exportStar(require("./utils"), exports);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ModelInfo } from '@bernierllc/ai-provider-core';
|
|
2
|
+
/**
|
|
3
|
+
* Claude 3 Model Registry
|
|
4
|
+
* Contains information about all available Claude models
|
|
5
|
+
*/
|
|
6
|
+
export declare class ClaudeModelRegistry {
|
|
7
|
+
private static models;
|
|
8
|
+
/**
|
|
9
|
+
* Initialize the model registry with Claude 3 models
|
|
10
|
+
*/
|
|
11
|
+
static initialize(): void;
|
|
12
|
+
/**
|
|
13
|
+
* Get a specific model by ID
|
|
14
|
+
*/
|
|
15
|
+
static getModel(modelId: string): ModelInfo | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Get all available models
|
|
18
|
+
*/
|
|
19
|
+
static getAllModels(): ModelInfo[];
|
|
20
|
+
/**
|
|
21
|
+
* Check if a model ID is valid
|
|
22
|
+
*/
|
|
23
|
+
static isValidModel(modelId: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Get pricing for a specific model
|
|
26
|
+
*/
|
|
27
|
+
static getModelPricing(modelId: string): {
|
|
28
|
+
inputPrice: number;
|
|
29
|
+
outputPrice: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code only within the scope of the project it was delivered for.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ClaudeModelRegistry = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* Claude 3 Model Registry
|
|
13
|
+
* Contains information about all available Claude models
|
|
14
|
+
*/
|
|
15
|
+
class ClaudeModelRegistry {
|
|
16
|
+
/**
|
|
17
|
+
* Initialize the model registry with Claude 3 models
|
|
18
|
+
*/
|
|
19
|
+
static initialize() {
|
|
20
|
+
const models = [
|
|
21
|
+
{
|
|
22
|
+
id: 'claude-3-opus-20240229',
|
|
23
|
+
name: 'Claude 3 Opus',
|
|
24
|
+
contextWindow: 200000,
|
|
25
|
+
maxOutputTokens: 4096,
|
|
26
|
+
pricing: {
|
|
27
|
+
inputPricePerToken: 15 / 1000000,
|
|
28
|
+
outputPricePerToken: 75 / 1000000,
|
|
29
|
+
currency: 'USD'
|
|
30
|
+
},
|
|
31
|
+
capabilities: ['chat', 'completion', 'vision', 'extended-context', 'analysis'],
|
|
32
|
+
description: 'Most capable Claude model for complex tasks with superior reasoning'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'claude-3-sonnet-20240229',
|
|
36
|
+
name: 'Claude 3 Sonnet',
|
|
37
|
+
contextWindow: 200000,
|
|
38
|
+
maxOutputTokens: 4096,
|
|
39
|
+
pricing: {
|
|
40
|
+
inputPricePerToken: 3 / 1000000,
|
|
41
|
+
outputPricePerToken: 15 / 1000000,
|
|
42
|
+
currency: 'USD'
|
|
43
|
+
},
|
|
44
|
+
capabilities: ['chat', 'completion', 'vision', 'extended-context'],
|
|
45
|
+
description: 'Balanced performance and speed for most tasks'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'claude-3-haiku-20240307',
|
|
49
|
+
name: 'Claude 3 Haiku',
|
|
50
|
+
contextWindow: 200000,
|
|
51
|
+
maxOutputTokens: 4096,
|
|
52
|
+
pricing: {
|
|
53
|
+
inputPricePerToken: 0.25 / 1000000,
|
|
54
|
+
outputPricePerToken: 1.25 / 1000000,
|
|
55
|
+
currency: 'USD'
|
|
56
|
+
},
|
|
57
|
+
capabilities: ['chat', 'completion', 'vision', 'extended-context'],
|
|
58
|
+
description: 'Fastest and most compact model for quick responses'
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
models.forEach(model => {
|
|
62
|
+
this.models.set(model.id, model);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get a specific model by ID
|
|
67
|
+
*/
|
|
68
|
+
static getModel(modelId) {
|
|
69
|
+
if (this.models.size === 0) {
|
|
70
|
+
this.initialize();
|
|
71
|
+
}
|
|
72
|
+
return this.models.get(modelId);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get all available models
|
|
76
|
+
*/
|
|
77
|
+
static getAllModels() {
|
|
78
|
+
if (this.models.size === 0) {
|
|
79
|
+
this.initialize();
|
|
80
|
+
}
|
|
81
|
+
return Array.from(this.models.values());
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check if a model ID is valid
|
|
85
|
+
*/
|
|
86
|
+
static isValidModel(modelId) {
|
|
87
|
+
if (this.models.size === 0) {
|
|
88
|
+
this.initialize();
|
|
89
|
+
}
|
|
90
|
+
return this.models.has(modelId);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get pricing for a specific model
|
|
94
|
+
*/
|
|
95
|
+
static getModelPricing(modelId) {
|
|
96
|
+
const model = this.getModel(modelId);
|
|
97
|
+
if (model?.pricing) {
|
|
98
|
+
return {
|
|
99
|
+
inputPrice: model.pricing.inputPricePerToken * 1000000,
|
|
100
|
+
outputPrice: model.pricing.outputPricePerToken * 1000000
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// Default to Sonnet pricing
|
|
104
|
+
return {
|
|
105
|
+
inputPrice: 3,
|
|
106
|
+
outputPrice: 15
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.ClaudeModelRegistry = ClaudeModelRegistry;
|
|
111
|
+
ClaudeModelRegistry.models = new Map();
|
|
112
|
+
// Initialize on module load
|
|
113
|
+
ClaudeModelRegistry.initialize();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { AIProviderConfig, CompletionRequest } from '@bernierllc/ai-provider-core';
|
|
2
|
+
/**
|
|
3
|
+
* Anthropic Provider Configuration
|
|
4
|
+
*/
|
|
5
|
+
export interface AnthropicProviderConfig extends AIProviderConfig {
|
|
6
|
+
providerName: 'anthropic';
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Anthropic Extended Context Request
|
|
10
|
+
*/
|
|
11
|
+
export interface AnthropicExtendedContextRequest extends CompletionRequest {
|
|
12
|
+
enableExtendedContext?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Anthropic Vision Request
|
|
16
|
+
*/
|
|
17
|
+
export interface AnthropicVisionRequest {
|
|
18
|
+
imageData: string | Buffer;
|
|
19
|
+
prompt: string;
|
|
20
|
+
model?: string;
|
|
21
|
+
maxTokens?: number;
|
|
22
|
+
temperature?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Anthropic Message Content Block
|
|
26
|
+
*/
|
|
27
|
+
export interface AnthropicMessageContent {
|
|
28
|
+
type: 'text' | 'image';
|
|
29
|
+
text?: string;
|
|
30
|
+
source?: {
|
|
31
|
+
type: 'base64';
|
|
32
|
+
media_type: string;
|
|
33
|
+
data: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Anthropic Message Format (Internal)
|
|
38
|
+
*/
|
|
39
|
+
export interface AnthropicMessage {
|
|
40
|
+
role: 'user' | 'assistant';
|
|
41
|
+
content: string | AnthropicMessageContent[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Anthropic Stop Reason
|
|
45
|
+
*/
|
|
46
|
+
export type AnthropicStopReason = 'end_turn' | 'max_tokens' | 'stop_sequence' | null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code only within the scope of the project it was delivered for.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './anthropic-types';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code only within the scope of the project it was delivered for.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
21
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
__exportStar(require("./anthropic-types"), exports);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle Anthropic API errors and convert to provider error format
|
|
3
|
+
*/
|
|
4
|
+
export declare function handleAnthropicError(error: unknown): Error;
|
|
5
|
+
/**
|
|
6
|
+
* Check if error is retryable
|
|
7
|
+
*/
|
|
8
|
+
export declare function isRetryableError(error: unknown): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Get retry delay from error (if available)
|
|
11
|
+
*/
|
|
12
|
+
export declare function getRetryDelay(error: unknown): number | undefined;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code only within the scope of the project it was delivered for.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.handleAnthropicError = handleAnthropicError;
|
|
14
|
+
exports.isRetryableError = isRetryableError;
|
|
15
|
+
exports.getRetryDelay = getRetryDelay;
|
|
16
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
17
|
+
/**
|
|
18
|
+
* Handle Anthropic API errors and convert to provider error format
|
|
19
|
+
*/
|
|
20
|
+
function handleAnthropicError(error) {
|
|
21
|
+
if (error instanceof sdk_1.default.APIError) {
|
|
22
|
+
const status = error.status || 'unknown';
|
|
23
|
+
const message = `Anthropic API Error (${status}): ${error.message}`;
|
|
24
|
+
const providerError = new Error(message);
|
|
25
|
+
providerError.name = 'AnthropicAPIError';
|
|
26
|
+
return providerError;
|
|
27
|
+
}
|
|
28
|
+
if (error instanceof sdk_1.default.AuthenticationError) {
|
|
29
|
+
const providerError = new Error('Anthropic authentication failed. Check your API key.');
|
|
30
|
+
providerError.name = 'AnthropicAuthError';
|
|
31
|
+
return providerError;
|
|
32
|
+
}
|
|
33
|
+
if (error instanceof sdk_1.default.RateLimitError) {
|
|
34
|
+
const providerError = new Error('Anthropic rate limit exceeded. Please retry after a delay.');
|
|
35
|
+
providerError.name = 'AnthropicRateLimitError';
|
|
36
|
+
return providerError;
|
|
37
|
+
}
|
|
38
|
+
if (error instanceof Error) {
|
|
39
|
+
return error;
|
|
40
|
+
}
|
|
41
|
+
return new Error(`Unknown Anthropic error: ${String(error)}`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check if error is retryable
|
|
45
|
+
*/
|
|
46
|
+
function isRetryableError(error) {
|
|
47
|
+
if (error instanceof sdk_1.default.APIError) {
|
|
48
|
+
// Retry on 5xx errors and rate limits
|
|
49
|
+
const status = error.status || 0;
|
|
50
|
+
return status >= 500 || status === 429;
|
|
51
|
+
}
|
|
52
|
+
if (error instanceof sdk_1.default.RateLimitError) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get retry delay from error (if available)
|
|
59
|
+
*/
|
|
60
|
+
function getRetryDelay(error) {
|
|
61
|
+
if (error instanceof sdk_1.default.RateLimitError) {
|
|
62
|
+
// Check for Retry-After header if available
|
|
63
|
+
// Default to 1 second if not specified
|
|
64
|
+
return 1000;
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|