@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.
@@ -0,0 +1,120 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code only within the scope of the project it was delivered for.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ import { ModelInfo } from '@bernierllc/ai-provider-core';
10
+
11
+ /**
12
+ * Claude 3 Model Registry
13
+ * Contains information about all available Claude models
14
+ */
15
+ export class ClaudeModelRegistry {
16
+ private static models: Map<string, ModelInfo> = new Map();
17
+
18
+ /**
19
+ * Initialize the model registry with Claude 3 models
20
+ */
21
+ static initialize(): void {
22
+ const models: ModelInfo[] = [
23
+ {
24
+ id: 'claude-3-opus-20240229',
25
+ name: 'Claude 3 Opus',
26
+ contextWindow: 200000,
27
+ maxOutputTokens: 4096,
28
+ pricing: {
29
+ inputPricePerToken: 15 / 1000000,
30
+ outputPricePerToken: 75 / 1000000,
31
+ currency: 'USD'
32
+ },
33
+ capabilities: ['chat', 'completion', 'vision', 'extended-context', 'analysis'],
34
+ description: 'Most capable Claude model for complex tasks with superior reasoning'
35
+ },
36
+ {
37
+ id: 'claude-3-sonnet-20240229',
38
+ name: 'Claude 3 Sonnet',
39
+ contextWindow: 200000,
40
+ maxOutputTokens: 4096,
41
+ pricing: {
42
+ inputPricePerToken: 3 / 1000000,
43
+ outputPricePerToken: 15 / 1000000,
44
+ currency: 'USD'
45
+ },
46
+ capabilities: ['chat', 'completion', 'vision', 'extended-context'],
47
+ description: 'Balanced performance and speed for most tasks'
48
+ },
49
+ {
50
+ id: 'claude-3-haiku-20240307',
51
+ name: 'Claude 3 Haiku',
52
+ contextWindow: 200000,
53
+ maxOutputTokens: 4096,
54
+ pricing: {
55
+ inputPricePerToken: 0.25 / 1000000,
56
+ outputPricePerToken: 1.25 / 1000000,
57
+ currency: 'USD'
58
+ },
59
+ capabilities: ['chat', 'completion', 'vision', 'extended-context'],
60
+ description: 'Fastest and most compact model for quick responses'
61
+ }
62
+ ];
63
+
64
+ models.forEach(model => {
65
+ this.models.set(model.id, model);
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Get a specific model by ID
71
+ */
72
+ static getModel(modelId: string): ModelInfo | undefined {
73
+ if (this.models.size === 0) {
74
+ this.initialize();
75
+ }
76
+ return this.models.get(modelId);
77
+ }
78
+
79
+ /**
80
+ * Get all available models
81
+ */
82
+ static getAllModels(): ModelInfo[] {
83
+ if (this.models.size === 0) {
84
+ this.initialize();
85
+ }
86
+ return Array.from(this.models.values());
87
+ }
88
+
89
+ /**
90
+ * Check if a model ID is valid
91
+ */
92
+ static isValidModel(modelId: string): boolean {
93
+ if (this.models.size === 0) {
94
+ this.initialize();
95
+ }
96
+ return this.models.has(modelId);
97
+ }
98
+
99
+ /**
100
+ * Get pricing for a specific model
101
+ */
102
+ static getModelPricing(modelId: string): { inputPrice: number; outputPrice: number } {
103
+ const model = this.getModel(modelId);
104
+ if (model?.pricing) {
105
+ return {
106
+ inputPrice: model.pricing.inputPricePerToken * 1000000,
107
+ outputPrice: model.pricing.outputPricePerToken * 1000000
108
+ };
109
+ }
110
+
111
+ // Default to Sonnet pricing
112
+ return {
113
+ inputPrice: 3,
114
+ outputPrice: 15
115
+ };
116
+ }
117
+ }
118
+
119
+ // Initialize on module load
120
+ ClaudeModelRegistry.initialize();
@@ -0,0 +1,60 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code only within the scope of the project it was delivered for.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ import { AIProviderConfig, CompletionRequest } from '@bernierllc/ai-provider-core';
10
+
11
+ /**
12
+ * Anthropic Provider Configuration
13
+ */
14
+ export interface AnthropicProviderConfig extends AIProviderConfig {
15
+ providerName: 'anthropic';
16
+ }
17
+
18
+ /**
19
+ * Anthropic Extended Context Request
20
+ */
21
+ export interface AnthropicExtendedContextRequest extends CompletionRequest {
22
+ enableExtendedContext?: boolean;
23
+ }
24
+
25
+ /**
26
+ * Anthropic Vision Request
27
+ */
28
+ export interface AnthropicVisionRequest {
29
+ imageData: string | Buffer;
30
+ prompt: string;
31
+ model?: string;
32
+ maxTokens?: number;
33
+ temperature?: number;
34
+ }
35
+
36
+ /**
37
+ * Anthropic Message Content Block
38
+ */
39
+ export interface AnthropicMessageContent {
40
+ type: 'text' | 'image';
41
+ text?: string;
42
+ source?: {
43
+ type: 'base64';
44
+ media_type: string;
45
+ data: string;
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Anthropic Message Format (Internal)
51
+ */
52
+ export interface AnthropicMessage {
53
+ role: 'user' | 'assistant';
54
+ content: string | AnthropicMessageContent[];
55
+ }
56
+
57
+ /**
58
+ * Anthropic Stop Reason
59
+ */
60
+ export type AnthropicStopReason = 'end_turn' | 'max_tokens' | 'stop_sequence' | null;
@@ -0,0 +1,9 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code only within the scope of the project it was delivered for.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ export * from './anthropic-types';
@@ -0,0 +1,71 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code only within the scope of the project it was delivered for.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ import Anthropic from '@anthropic-ai/sdk';
10
+
11
+ /**
12
+ * Handle Anthropic API errors and convert to provider error format
13
+ */
14
+ export function handleAnthropicError(error: unknown): Error {
15
+ if (error instanceof Anthropic.APIError) {
16
+ const status = error.status || 'unknown';
17
+ const message = `Anthropic API Error (${status}): ${error.message}`;
18
+
19
+ const providerError = new Error(message);
20
+ providerError.name = 'AnthropicAPIError';
21
+ return providerError;
22
+ }
23
+
24
+ if (error instanceof Anthropic.AuthenticationError) {
25
+ const providerError = new Error('Anthropic authentication failed. Check your API key.');
26
+ providerError.name = 'AnthropicAuthError';
27
+ return providerError;
28
+ }
29
+
30
+ if (error instanceof Anthropic.RateLimitError) {
31
+ const providerError = new Error('Anthropic rate limit exceeded. Please retry after a delay.');
32
+ providerError.name = 'AnthropicRateLimitError';
33
+ return providerError;
34
+ }
35
+
36
+ if (error instanceof Error) {
37
+ return error;
38
+ }
39
+
40
+ return new Error(`Unknown Anthropic error: ${String(error)}`);
41
+ }
42
+
43
+ /**
44
+ * Check if error is retryable
45
+ */
46
+ export function isRetryableError(error: unknown): boolean {
47
+ if (error instanceof Anthropic.APIError) {
48
+ // Retry on 5xx errors and rate limits
49
+ const status = error.status || 0;
50
+ return status >= 500 || status === 429;
51
+ }
52
+
53
+ if (error instanceof Anthropic.RateLimitError) {
54
+ return true;
55
+ }
56
+
57
+ return false;
58
+ }
59
+
60
+ /**
61
+ * Get retry delay from error (if available)
62
+ */
63
+ export function getRetryDelay(error: unknown): number | undefined {
64
+ if (error instanceof Anthropic.RateLimitError) {
65
+ // Check for Retry-After header if available
66
+ // Default to 1 second if not specified
67
+ return 1000;
68
+ }
69
+
70
+ return undefined;
71
+ }
@@ -0,0 +1,10 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code only within the scope of the project it was delivered for.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ export * from './message-conversion';
10
+ export * from './error-handling';
@@ -0,0 +1,78 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code only within the scope of the project it was delivered for.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ import { Message } from '@bernierllc/ai-provider-core';
10
+ import Anthropic from '@anthropic-ai/sdk';
11
+
12
+ /**
13
+ * Convert messages from unified format to Anthropic format
14
+ * Extracts system messages separately (Anthropic uses system parameter, not messages array)
15
+ */
16
+ export function convertMessagesToAnthropicFormat(messages: Message[]): {
17
+ system?: string;
18
+ messages: Anthropic.MessageParam[];
19
+ } {
20
+ // Extract system messages
21
+ const systemMessages = messages.filter(m => m.role === 'system');
22
+ const system = systemMessages.length > 0
23
+ ? systemMessages.map(m => m.content).join('\n\n')
24
+ : undefined;
25
+
26
+ // Convert remaining messages to Anthropic format
27
+ const anthropicMessages: Anthropic.MessageParam[] = messages
28
+ .filter(m => m.role !== 'system')
29
+ .map(msg => ({
30
+ role: msg.role === 'assistant' ? ('assistant' as const) : ('user' as const),
31
+ content: msg.content
32
+ }));
33
+
34
+ return { system, messages: anthropicMessages };
35
+ }
36
+
37
+ /**
38
+ * Validate message format before conversion
39
+ */
40
+ export function validateMessages(messages: Message[]): {
41
+ isValid: boolean;
42
+ error?: string;
43
+ } {
44
+ if (!messages || messages.length === 0) {
45
+ return {
46
+ isValid: false,
47
+ error: 'Messages array cannot be empty'
48
+ };
49
+ }
50
+
51
+ // Check that all messages have required fields
52
+ for (const msg of messages) {
53
+ if (!msg.role || !msg.content) {
54
+ return {
55
+ isValid: false,
56
+ error: 'All messages must have role and content'
57
+ };
58
+ }
59
+
60
+ if (!['system', 'user', 'assistant'].includes(msg.role)) {
61
+ return {
62
+ isValid: false,
63
+ error: `Invalid message role: ${msg.role}`
64
+ };
65
+ }
66
+ }
67
+
68
+ // Ensure non-system messages exist
69
+ const nonSystemMessages = messages.filter(m => m.role !== 'system');
70
+ if (nonSystemMessages.length === 0) {
71
+ return {
72
+ isValid: false,
73
+ error: 'At least one non-system message is required'
74
+ };
75
+ }
76
+
77
+ return { isValid: true };
78
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "declaration": true,
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "moduleResolution": "node",
15
+ "noImplicitAny": true,
16
+ "strictNullChecks": true,
17
+ "strictFunctionTypes": true,
18
+ "strictBindCallApply": true,
19
+ "strictPropertyInitialization": true,
20
+ "noImplicitThis": true,
21
+ "alwaysStrict": true,
22
+ "noUnusedLocals": true,
23
+ "noUnusedParameters": true,
24
+ "noImplicitReturns": true,
25
+ "noFallthroughCasesInSwitch": true
26
+ },
27
+ "include": ["src/**/*"],
28
+ "exclude": ["node_modules", "dist", "__tests__"]
29
+ }