llm-credit-sdk
Version:
SDK for estimating and reconciling LLM token costs using a credit system.
439 lines (347 loc) • 11.3 kB
Markdown
# LLM Credit SDK
A TypeScript SDK for tracking AI token usage and credit estimation, designed for solo developers and startups monetizing their apps via usage-based billing.
## Features
✅ **Stateless & Lightweight** - No local storage or persistence
✅ **Credit Estimation** - Estimate costs before making LLM calls
✅ **Usage Reconciliation** - Track actual vs estimated usage
✅ **Multi-Provider Support** - OpenAI, Anthropic, Together AI, Cohere
✅ **Custom Pricing** - Override default pricing with your own margins
✅ **TypeScript First** - Full type safety and IntelliSense support
✅ **Framework Agnostic** - Works with any Node.js/TypeScript project
## Installation
```bash
npm install llm-credit-sdk
```
## Quick Start
```typescript
import { LLMCreditSDK } from 'llm-credit-sdk';
// Initialize SDK
const sdk = new LLMCreditSDK();
// Estimate credits before making a call
const estimate = sdk.estimateCredits({
model: 'openai:gpt-4',
feature: 'chat',
promptTokens: 150,
completionTokens: 350
});
console.log(`Estimated credits: ${estimate.estimatedCredits}`);
// Reconcile actual usage after the call
const reconciliation = sdk.reconcile({
model: 'openai:gpt-4',
feature: 'chat',
promptTokens: 150,
completionTokens: 350,
actualPromptTokens: 160,
actualCompletionTokens: 340
});
console.log('Usage delta:', reconciliation.estimatedVsActualCreditDelta);
```
## Core Concepts
### Token
Unit of measurement used by LLM providers to charge for usage.
### Credit
Unit of usage abstraction exposed to your app users. Credits = Token Cost × Margin.
### Margin
Multiplier added on top of token cost to form credit cost. Used to control profitability.
### Feature
A use case or endpoint in your app (e.g., `chat`, `summarize`, `generate_code`).
## API Reference
### `estimateCredits(input)`
Estimate credits for an LLM call before making it.
```typescript
interface EstimateCreditsInput {
model: string; // e.g., 'openai:gpt-4'
feature: string; // e.g., 'chat'
promptTokens: number; // Estimated prompt tokens
completionTokens: number; // Estimated completion tokens
}
interface EstimateCreditsResult {
estimatedCredits: number;
}
```
### `reconcile(input)`
Reconcile estimated vs actual usage after an LLM call.
```typescript
interface ReconcileInput {
model: string;
feature: string;
promptTokens: number; // Original estimate
completionTokens: number; // Original estimate
actualPromptTokens: number; // Actual usage
actualCompletionTokens: number; // Actual usage
}
interface ReconcileResult {
estimatedCredits: number; // Original estimate
actualTokensUsed: number; // Total actual tokens
actualCost: number; // Actual cost in dollars
estimatedVsActualCreditDelta: number; // Difference (+ = underestimated)
}
```
### `wrapCall(input)`
Wrap an LLM call with automatic estimation and reconciliation.
```typescript
interface WrapCallInput<T> {
model: string;
feature: string;
promptTokens: number;
completionTokens: number;
callFunction: () => Promise<T>;
tokenExtractor?: TokenExtractor<T>;
}
interface WrapCallResult<T> {
response: T;
reconciliation: ReconcileResult;
}
```
**Example:**
```typescript
import { TokenExtractor, openAIChatExtractor, OpenAIChatCompletionResponse } from 'llm-credit-sdk';
// Using the built-in OpenAI extractor (recommended)
const result = await sdk.wrapCall({
model: 'openai:gpt-4',
feature: 'chat',
promptTokens: 150,
completionTokens: 350,
callFunction: async () => {
return await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello!' }]
});
},
tokenExtractor: openAIChatExtractor
});
// Or create a custom extractor for specific needs
const customExtractor: TokenExtractor<OpenAIChatCompletionResponse> = {
providerName: 'openai',
description: 'OpenAI Chat Completions API',
apiVersion: 'v1',
extract: (response) => ({
promptTokens: response.usage?.prompt_tokens || 0,
completionTokens: response.usage?.completion_tokens || 0
})
};
console.log('Response:', result.response);
console.log('Credits used:', result.reconciliation.actualTokensUsed);
```
> **📖 Learn More**: For detailed information about creating and using token extractors, see our [Token Extractors Guide](docs/token-extractors.md).
## Supported Models
### OpenAI
- `openai:gpt-4`
- `openai:gpt-4-turbo`
- `openai:gpt-3.5-turbo`
### Anthropic
- `anthropic:claude-3-opus`
- `anthropic:claude-3-sonnet`
- `anthropic:claude-3-haiku`
### Together AI
- `together:llama3-8b`
- `together:llama3-70b`
### Cohere
- `cohere:command`
## Type Safety & Custom Models
### Type-Safe Usage (Recommended)
The SDK provides full TypeScript type safety for supported models and features:
```typescript
import { LLMCreditSDK, SupportedModel, SupportedFeature } from 'llm-credit-sdk';
const sdk = new LLMCreditSDK();
// TypeScript will autocomplete and validate these values
const estimate = sdk.estimateCredits({
model: 'openai:gpt-4', // ✅ TypeScript suggests all supported models
feature: 'chat', // ✅ TypeScript validates available features
promptTokens: 100,
completionTokens: 50
});
```
**Supported Models & Features:**
- **Models**: `openai:gpt-4`, `openai:gpt-4-turbo`, `openai:gpt-3.5-turbo`, `anthropic:claude-3-opus`, `anthropic:claude-3-sonnet`, `anthropic:claude-3-haiku`, `together:llama3-8b`, `together:llama3-70b`, `cohere:command`
- **Features**: `chat`, `summarize`, `generate_code`, `translate`
### Using Custom Models & Features
To use models or features not in the default list, you need to configure them and use explicit typing:
```typescript
import { LLMCreditSDK, WrapCallInputCustom } from 'llm-credit-sdk';
// 1. Configure SDK with custom models
const sdk = new LLMCreditSDK({
models: {
'custom:my-model': {
prompt_cost_per_1k: 0.005,
completion_cost_per_1k: 0.01,
features: {
'document_analysis': { margin: 2.5 }
}
}
}
});
// 2. Use explicit typing for custom models
const result = await sdk.wrapCall({
model: 'custom:my-model',
feature: 'document_analysis',
promptTokens: 500,
completionTokens: 200,
callFunction: async () => {
// Your custom AI service call
return await myCustomAI.analyze(document);
}
} as WrapCallInputCustom);
```
### Best Practices for Custom Models
Create type-safe wrappers for your custom models:
```typescript
// Define your custom types
type MyCustomModel = 'custom:my-model' | 'custom:another-model';
type MyCustomFeatures = 'document_analysis' | 'data_extraction';
// Create a type-safe wrapper function
async function callCustomModel(
model: MyCustomModel,
feature: MyCustomFeatures,
promptTokens: number,
completionTokens: number,
callFunction: () => Promise<any>
) {
return sdk.wrapCall({
model,
feature,
promptTokens,
completionTokens,
callFunction
} as WrapCallInputCustom);
}
// Now you have type safety for your custom models
const result = await callCustomModel(
'custom:my-model', // ✅ TypeScript validates this
'document_analysis', // ✅ TypeScript validates this
500,
200,
async () => await myAI.analyze(doc)
);
```
## Custom Configuration
Override default pricing and add your own models:
```typescript
import { LLMCreditSDK } from 'llm-credit-sdk';
const sdk = new LLMCreditSDK({
default_margin: 2.0, // Higher default margin
models: {
'custom:my-model': {
prompt_cost_per_1k: 0.005,
completion_cost_per_1k: 0.01,
features: {
'my_feature': { margin: 3.0 }
}
},
// Override existing model pricing
'openai:gpt-4': {
prompt_cost_per_1k: 0.035, // Custom pricing
completion_cost_per_1k: 0.065,
features: {
'chat': { margin: 2.5 } // Higher margin for chat
}
}
}
});
```
## Utility Methods
```typescript
// Get available models
const models = sdk.getAvailableModels();
console.log('Available models:', models);
// Get available features for a model
const features = sdk.getAvailableFeatures('openai:gpt-4');
console.log('GPT-4 features:', features);
// Get current configuration
const config = sdk.getConfig();
console.log('Current config:', config);
```
## Error Handling
The SDK throws descriptive errors for invalid configurations:
```typescript
try {
sdk.estimateCredits({
model: 'nonexistent:model',
feature: 'chat',
promptTokens: 100,
completionTokens: 200
});
} catch (error) {
console.error(error.message);
// "Model 'nonexistent:model' not found in configuration. Available models: ..."
}
```
## Real-World Integration Examples
### With OpenAI SDK
```typescript
import OpenAI from 'openai';
import { LLMCreditSDK, openAIChatExtractor } from 'llm-credit-sdk';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const sdk = new LLMCreditSDK();
async function chatWithGPT(message: string, userId: string) {
const result = await sdk.wrapCall({
model: 'openai:gpt-4',
feature: 'chat',
promptTokens: 100, // Estimate based on message length
completionTokens: 200, // Conservative estimate
callFunction: async () => {
return await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: message }]
});
},
tokenExtractor: openAIChatExtractor
});
// Log usage for billing
console.log(`User ${userId} used ${result.reconciliation.actualTokensUsed} credits`);
return result.response.choices[0].message.content;
}
```
### With Express.js API
```typescript
import express from 'express';
import { LLMCreditSDK } from 'llm-credit-sdk';
const app = express();
const sdk = new LLMCreditSDK();
app.post('/api/summarize', async (req, res) => {
const { text, userId } = req.body;
try {
// Estimate tokens (you might use a tokenizer library)
const estimatedTokens = Math.ceil(text.length / 4);
const result = await sdk.wrapCall({
model: 'openai:gpt-3.5-turbo',
feature: 'summarize',
promptTokens: estimatedTokens,
completionTokens: estimatedTokens * 0.3, // Summaries are typically shorter
callFunction: async () => {
// Your LLM call here
return await summarizeText(text);
}
});
// Send usage data to your billing system
await logUsage(userId, result.reconciliation);
res.json({
summary: result.response,
creditsUsed: result.reconciliation.actualTokensUsed
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
```
## Performance Considerations
- ✅ **Synchronous operations** - All calculations are done in-memory
- ✅ **No I/O operations** - No file system or network calls
- ✅ **Lightweight** - Minimal memory footprint
- ✅ **Serverless friendly** - Works in edge environments
- ✅ **No side effects** - Pure functions, predictable behavior
## Development
```bash
# Install dependencies
npm install
# Build the project
npm run build
# Run the example
npm run example
# Watch mode for development
npm run dev
```
## License
MIT License - see LICENSE file for details.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.