@bernierllc/anthropic-client
Version:
Type-safe Anthropic Claude API client with automatic rate limiting, retry logic, streaming support, and cost tracking
318 lines (234 loc) • 9.24 kB
Markdown
# @bernierllc/anthropic-client
Type-safe Anthropic Claude API client with automatic rate limiting, retry logic, streaming support, and cost tracking.
## Installation
```bash
npm install @bernierllc/anthropic-client
```
## Features
- **Type-safe API**: Full TypeScript support with strict typing
- **Multiple Claude Models**: Support for Claude 3 Opus, Sonnet, Haiku, and Sonnet 3.5
- **Streaming Support**: Real-time streaming responses with chunk callbacks
- **Rate Limiting**: Built-in rate limiting to respect API quotas
- **Automatic Retries**: Exponential backoff retry logic for failed requests
- **Cost Tracking**: Automatic tracking of token usage and costs
- **Comprehensive Logging**: Integration with @bernierllc/logger
- **Error Handling**: Structured error responses for robust error handling
## Usage
### Basic Completion
```typescript
import { AnthropicClient, ClaudeModel } from '@bernierllc/anthropic-client';
const client = new AnthropicClient({
apiKey: process.env.ANTHROPIC_API_KEY!
});
const result = await client.complete('Explain quantum computing in simple terms', {
model: ClaudeModel.SONNET,
maxTokens: 1024,
temperature: 0.7
});
if (result.success) {
console.log('Response:', result.content);
console.log('Tokens used:', result.usage?.totalTokens);
console.log('Cost:', `$${result.cost?.totalCost.toFixed(6)}`);
} else {
console.error('Error:', result.error);
}
```
### Streaming Response
```typescript
const result = await client.stream(
'Write a short story about space exploration',
(chunk) => {
// Process each chunk as it arrives
process.stdout.write(chunk);
},
{
model: ClaudeModel.OPUS,
maxTokens: 2000
}
);
console.log('\n\nTotal tokens:', result.usage?.totalTokens);
console.log('Total cost:', `$${result.cost?.totalCost.toFixed(6)}`);
```
### Custom Configuration
```typescript
const client = new AnthropicClient({
apiKey: process.env.ANTHROPIC_API_KEY!,
defaultModel: ClaudeModel.HAIKU,
maxRetries: 5,
rateLimit: {
requestsPerMinute: 100,
tokensPerMinute: 50000
},
enableLogging: true
});
```
### Disable Rate Limiting
```typescript
const client = new AnthropicClient({
apiKey: process.env.ANTHROPIC_API_KEY!,
rateLimit: null // Disable rate limiting
});
```
### Usage Tracking
```typescript
// Make multiple requests
await client.complete('First request');
await client.complete('Second request');
await client.complete('Third request');
// Get aggregated usage statistics
const usage = client.getUsage();
console.log('Total requests:', usage.totalRequests);
console.log('Total tokens:', usage.totalTokens);
console.log('Total cost:', `$${usage.totalCost.toFixed(6)}`);
console.log('Requests by model:', usage.requestsByModel);
// Reset usage statistics
client.resetUsage();
```
### Rate Limit Status
```typescript
const status = client.getRateLimitStatus();
if (status) {
console.log('Requests remaining:', status.requestsRemaining);
console.log('Reset time:', status.resetTime);
}
```
## API Reference
### AnthropicClient
#### Constructor
```typescript
new AnthropicClient(config: AnthropicClientConfig)
```
**Config Options:**
- `apiKey` (string, required): Your Anthropic API key
- `defaultModel` (ClaudeModel, optional): Default model to use (default: `ClaudeModel.SONNET`)
- `maxRetries` (number, optional): Maximum number of retry attempts (default: 3)
- `rateLimit` (object | null, optional): Rate limiting configuration or null to disable (default: 50 requests/minute)
- `requestsPerMinute` (number): Maximum requests per minute
- `tokensPerMinute` (number): Maximum tokens per minute (not enforced by current rate limiter)
- `enableLogging` (boolean, optional): Enable detailed logging (default: true)
#### Methods
##### complete(prompt: string, options?: CompletionOptions): Promise<CompletionResult>
Send a completion request to Claude.
**Options:**
- `model` (ClaudeModel): Model to use for this request
- `maxTokens` (number): Maximum tokens to generate (default: 1024)
- `temperature` (number): Sampling temperature (0-1)
- `topP` (number): Nucleus sampling parameter
- `topK` (number): Top-k sampling parameter
- `stopSequences` (string[]): Sequences that stop generation
- `systemPrompt` (string): System prompt to use
- `metadata` (Record<string, unknown>): Additional metadata
**Returns:** `CompletionResult` object with:
- `success` (boolean): Whether the request succeeded
- `content` (string | undefined): Generated text
- `usage` (object | undefined): Token usage information
- `cost` (object | undefined): Cost breakdown
- `model` (string | undefined): Model used
- `stopReason` (string | undefined): Why generation stopped
- `error` (string | undefined): Error message if failed
##### stream(prompt: string, onChunk: StreamChunkCallback, options?: CompletionOptions): Promise<CompletionResult>
Stream a completion response from Claude.
**Parameters:**
- `prompt` (string): The prompt to send
- `onChunk` (function): Callback function called for each text chunk
- `options` (CompletionOptions): Same options as `complete()`
**Returns:** `CompletionResult` with the complete response
##### getUsage(): UsageStats
Get cumulative usage statistics.
**Returns:**
- `totalRequests` (number): Total number of requests made
- `totalInputTokens` (number): Total input tokens used
- `totalOutputTokens` (number): Total output tokens generated
- `totalTokens` (number): Total tokens (input + output)
- `totalCost` (number): Total cost in USD
- `requestsByModel` (Record<string, number>): Request count by model
##### resetUsage(): void
Reset all usage statistics to zero.
##### getRateLimitStatus(): RateLimitStatus | null
Get current rate limit status.
**Returns:**
- `requestsRemaining` (number): Requests remaining in current window
- `tokensRemaining` (number): Tokens remaining (not implemented)
- `resetTime` (Date): When the rate limit window resets
Returns `null` if rate limiting is disabled.
## Claude Models
The following models are supported:
- `ClaudeModel.OPUS` - claude-3-opus-20240229 (most capable, highest cost)
- `ClaudeModel.SONNET` - claude-3-sonnet-20240229 (balanced performance and cost)
- `ClaudeModel.SONNET_3_5` - claude-3-5-sonnet-20241022 (latest Sonnet version)
- `ClaudeModel.HAIKU` - claude-3-haiku-20240307 (fastest, lowest cost)
## Cost Tracking
The client automatically tracks costs based on current Anthropic pricing:
| Model | Input Cost (per 1M tokens) | Output Cost (per 1M tokens) |
|-------|---------------------------|------------------------------|
| Opus | $15.00 | $75.00 |
| Sonnet | $3.00 | $15.00 |
| Sonnet 3.5 | $3.00 | $15.00 |
| Haiku | $0.25 | $1.25 |
## Error Handling
The client uses structured error responses:
```typescript
const result = await client.complete('test prompt');
if (!result.success) {
console.error('Request failed:', result.error);
// Handle error appropriately
} else {
// Process successful response
console.log(result.content);
}
```
## Integration Status
- **Logger**: ✅ Integrated - Uses @bernierllc/logger for structured logging of API calls, errors, and usage tracking
- **Docs-Suite**: ✅ Ready - Full TypeDoc documentation with API reference and usage examples
- **NeverHub**: 🔄 Optional - Supports graceful degradation. When NeverHub is available via @bernierllc/neverhub-adapter, the client can:
- Emit `ai.request.started` and `ai.request.completed` events
- Publish token usage and cost metrics
- Subscribe to configuration updates
- Register as an AI capability provider for service discovery
- All core functionality works without NeverHub present
### NeverHub Integration Example
```typescript
import { AnthropicClient } from '@bernierllc/anthropic-client';
import { NeverHubAdapter } from '@bernierllc/neverhub-adapter';
const client = new AnthropicClient({
apiKey: process.env.ANTHROPIC_API_KEY!
});
// Auto-detect and integrate with NeverHub if available
if (await NeverHubAdapter.detect()) {
const neverhub = new NeverHubAdapter();
await neverhub.register({
type: 'ai-provider',
name: '@bernierllc/anthropic-client',
version: '1.0.0',
capabilities: [
{ type: 'ai', name: 'text-completion', version: '1.0.0' },
{ type: 'ai', name: 'streaming', version: '1.0.0' }
]
});
// Client emits events automatically when integrated
}
// Works identically with or without NeverHub
const result = await client.complete('Your prompt here');
```
## Development
```bash
# Install dependencies
npm install
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Build
npm run build
# Lint
npm run lint
```
## License
Copyright (c) 2025 Bernier LLC
This file is licensed to the client under a limited-use license.
The client may use and modify this code *only within the scope of the project it was delivered for*.
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
## See Also
- [@bernierllc/logger](../logger) - Structured logging
- [@bernierllc/retry-policy](../retry-policy) - Retry logic with exponential backoff
- [Anthropic API Documentation](https://docs.anthropic.com/) - Official API documentation