llm-price-tracker 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.md +22 -0
- package/README.ko.md +270 -0
- package/README.md +270 -0
- package/dist/adapters/index.d.ts +8 -0
- package/dist/adapters/index.js +12 -0
- package/dist/adapters/mongo.d.ts +15 -0
- package/dist/adapters/mongo.js +71 -0
- package/dist/adapters/mysql.d.ts +18 -0
- package/dist/adapters/mysql.js +97 -0
- package/dist/adapters/postgres.d.ts +21 -0
- package/dist/adapters/postgres.js +162 -0
- package/dist/adapters/typeorm.d.ts +13 -0
- package/dist/adapters/typeorm.js +60 -0
- package/dist/client.d.ts +63 -0
- package/dist/client.js +627 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +11 -0
- package/dist/pricing.d.ts +6 -0
- package/dist/pricing.js +49 -0
- package/dist/types.d.ts +124 -0
- package/dist/types.js +3 -0
- package/package.json +68 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
The MIT License (MIT)
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Jihong Min
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
package/README.ko.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# LLM Tracker SDK
|
|
2
|
+
|
|
3
|
+
팀 및 프로젝트별 LLM API 사용량을 추적하는 백엔드 SDK입니다. OpenAI, Claude, Gemini, Azure OpenAI, AWS Bedrock의 토큰 소비량, 비용, API 호출을 모니터링합니다.
|
|
4
|
+
|
|
5
|
+
> **목적**: 회사에서 팀별 또는 프로젝트별 LLM 사용량을 추적하고 분석하기 위해 만들어졌습니다. 비용 관리와 API 소비 현황 파악에 활용하세요.
|
|
6
|
+
|
|
7
|
+
> **주의**: 이 SDK는 **서버 사이드 전용**입니다. Node.js가 필요하며 브라우저에서는 실행할 수 없습니다.
|
|
8
|
+
|
|
9
|
+
## 주요 기능
|
|
10
|
+
|
|
11
|
+
- **다중 프로바이더 지원**: OpenAI, Claude, Gemini, Azure OpenAI, AWS Bedrock
|
|
12
|
+
- **자동 비용 계산**: LiteLLM에서 실시간 가격 정보 가져옴 (24시간마다 갱신)
|
|
13
|
+
- **다양한 DB 어댑터**: PostgreSQL, MySQL, MongoDB, TypeORM
|
|
14
|
+
- **스트리밍 지원**: OpenAI, Claude, Azure에서 토큰 추적과 함께 스트리밍
|
|
15
|
+
- **배치 처리**: 트래킹 데이터를 큐에 쌓아서 일괄 저장
|
|
16
|
+
- **재시도 로직**: 지수 백오프로 자동 재시도
|
|
17
|
+
- **사용량 통계**: 프로젝트, 프로바이더, 모델, 날짜 범위별 통계 조회
|
|
18
|
+
|
|
19
|
+
## 설치
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install llm-tracker-sdk
|
|
23
|
+
|
|
24
|
+
# 사용할 DB 드라이버 설치
|
|
25
|
+
npm install pg # PostgreSQL
|
|
26
|
+
npm install mysql2 # MySQL
|
|
27
|
+
npm install mongodb # MongoDB
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 빠른 시작
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { LLMTrackerSDK, PostgresAdapter } from 'llm-tracker-sdk';
|
|
34
|
+
|
|
35
|
+
const sdk = new LLMTrackerSDK({
|
|
36
|
+
projectName: 'my-project',
|
|
37
|
+
apiKeys: {
|
|
38
|
+
openai: process.env.OPENAI_API_KEY,
|
|
39
|
+
claude: process.env.ANTHROPIC_API_KEY,
|
|
40
|
+
},
|
|
41
|
+
database: new PostgresAdapter({
|
|
42
|
+
connectionString: process.env.DATABASE_URL,
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
await sdk.connect();
|
|
47
|
+
|
|
48
|
+
const response = await sdk.openai.chat.completions.create({
|
|
49
|
+
model: 'gpt-4o',
|
|
50
|
+
messages: [{ role: 'user', content: '안녕하세요!' }],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await sdk.disconnect();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 프로바이더
|
|
57
|
+
|
|
58
|
+
### OpenAI
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
const response = await sdk.openai.chat.completions.create({
|
|
62
|
+
model: 'gpt-4o',
|
|
63
|
+
messages: [{ role: 'user', content: '안녕하세요!' }],
|
|
64
|
+
response_format: { type: 'json_object' },
|
|
65
|
+
tools: [{ type: 'function', function: { name: 'get_weather', ... } }],
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Claude
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
const response = await sdk.claude.messages.create({
|
|
73
|
+
model: 'claude-sonnet-4-20250514',
|
|
74
|
+
max_tokens: 1024,
|
|
75
|
+
messages: [{ role: 'user', content: '안녕하세요!' }],
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Azure OpenAI
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const sdk = new LLMTrackerSDK({
|
|
83
|
+
projectName: 'my-project',
|
|
84
|
+
apiKeys: {
|
|
85
|
+
azure: {
|
|
86
|
+
apiKey: process.env.AZURE_OPENAI_API_KEY,
|
|
87
|
+
endpoint: process.env.AZURE_OPENAI_ENDPOINT,
|
|
88
|
+
deployment: 'gpt-4o',
|
|
89
|
+
apiVersion: '2024-02-15-preview',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
database: new PostgresAdapter({ ... }),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const response = await sdk.azure.chat.completions.create({
|
|
96
|
+
model: 'gpt-4o',
|
|
97
|
+
messages: [{ role: 'user', content: '안녕하세요!' }],
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### AWS Bedrock
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
const sdk = new LLMTrackerSDK({
|
|
105
|
+
projectName: 'my-project',
|
|
106
|
+
apiKeys: {
|
|
107
|
+
bedrock: {
|
|
108
|
+
region: 'us-east-1',
|
|
109
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
110
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
database: new PostgresAdapter({ ... }),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const response = await sdk.bedrock.chat({
|
|
117
|
+
modelId: 'anthropic.claude-3-5-sonnet-20241022-v2:0',
|
|
118
|
+
messages: [{ role: 'user', content: '안녕하세요!' }],
|
|
119
|
+
system: 'You are a helpful assistant.',
|
|
120
|
+
maxTokens: 1024,
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Gemini
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const response = await sdk.gemini.generateContent({
|
|
128
|
+
model: 'gemini-1.5-flash',
|
|
129
|
+
contents: [{ role: 'user', parts: [{ text: '안녕하세요!' }] }],
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## 스트리밍
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const stream = sdk.openai.chat.completions.stream({
|
|
137
|
+
model: 'gpt-4o',
|
|
138
|
+
messages: [{ role: 'user', content: '안녕하세요!' }],
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
for await (const chunk of stream) {
|
|
142
|
+
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## DB 어댑터
|
|
147
|
+
|
|
148
|
+
### PostgreSQL
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { PostgresAdapter } from 'llm-tracker-sdk';
|
|
152
|
+
|
|
153
|
+
new PostgresAdapter({
|
|
154
|
+
connectionString: 'postgres://user:pass@localhost:5432/db',
|
|
155
|
+
tableName: 'llm_tracking',
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### MySQL
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { MySQLAdapter } from 'llm-tracker-sdk';
|
|
163
|
+
|
|
164
|
+
new MySQLAdapter({
|
|
165
|
+
host: 'localhost',
|
|
166
|
+
port: 3306,
|
|
167
|
+
database: 'mydb',
|
|
168
|
+
user: 'root',
|
|
169
|
+
password: 'password',
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### MongoDB
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { MongoAdapter } from 'llm-tracker-sdk';
|
|
177
|
+
|
|
178
|
+
new MongoAdapter({
|
|
179
|
+
uri: 'mongodb://localhost:27017',
|
|
180
|
+
database: 'llm_tracking',
|
|
181
|
+
collection: 'api_usage',
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 커스텀 어댑터
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { DatabaseAdapter, TrackingData } from 'llm-tracker-sdk';
|
|
189
|
+
|
|
190
|
+
class MyAdapter implements DatabaseAdapter {
|
|
191
|
+
async connect(): Promise<void> { /* ... */ }
|
|
192
|
+
async disconnect(): Promise<void> { /* ... */ }
|
|
193
|
+
async save(data: TrackingData): Promise<void> { /* ... */ }
|
|
194
|
+
async saveBatch?(data: TrackingData[]): Promise<void> { /* ... */ }
|
|
195
|
+
async getStats?(filter: StatsFilter): Promise<UsageStats> { /* ... */ }
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## 설정
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const sdk = new LLMTrackerSDK({
|
|
203
|
+
projectName: 'my-project',
|
|
204
|
+
apiKeys: { ... },
|
|
205
|
+
database: new PostgresAdapter({ ... }),
|
|
206
|
+
|
|
207
|
+
// 커스텀 가격 (LiteLLM 가격 덮어쓰기)
|
|
208
|
+
customPricing: {
|
|
209
|
+
'my-custom-model': { inputCostPerToken: 0.00001, outputCostPerToken: 0.00002 },
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
// 재시도 설정
|
|
213
|
+
retry: {
|
|
214
|
+
maxRetries: 3,
|
|
215
|
+
initialDelayMs: 1000,
|
|
216
|
+
maxDelayMs: 10000,
|
|
217
|
+
backoffMultiplier: 2,
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
// 배치 처리
|
|
221
|
+
batchSize: 10,
|
|
222
|
+
batchIntervalMs: 5000,
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## 사용량 통계
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
const stats = await sdk.getStats({
|
|
230
|
+
projectName: 'my-project',
|
|
231
|
+
provider: 'openai',
|
|
232
|
+
startDate: new Date('2025-01-01'),
|
|
233
|
+
endDate: new Date('2025-01-31'),
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
console.log(stats);
|
|
237
|
+
// {
|
|
238
|
+
// totalRequests: 1000, // 총 요청 수
|
|
239
|
+
// successfulRequests: 995, // 성공한 요청 수
|
|
240
|
+
// failedRequests: 5, // 실패한 요청 수
|
|
241
|
+
// totalTokens: 500000, // 총 토큰 수
|
|
242
|
+
// totalPromptTokens: 200000, // 프롬프트 토큰 수
|
|
243
|
+
// totalCompletionTokens: 300000, // 응답 토큰 수
|
|
244
|
+
// totalCostUsd: 15.50, // 총 비용 (USD)
|
|
245
|
+
// avgLatencyMs: 850 // 평균 응답 시간 (ms)
|
|
246
|
+
// }
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## 저장되는 데이터
|
|
250
|
+
|
|
251
|
+
각 API 호출 시 자동으로 저장되는 데이터:
|
|
252
|
+
|
|
253
|
+
| 필드 | 설명 |
|
|
254
|
+
|------|------|
|
|
255
|
+
| `requestId` | 고유 요청 ID |
|
|
256
|
+
| `projectName` | 프로젝트 식별자 |
|
|
257
|
+
| `provider` | openai, claude, gemini, azure, bedrock |
|
|
258
|
+
| `model` | 모델명 |
|
|
259
|
+
| `promptTokens` | 입력 토큰 수 |
|
|
260
|
+
| `completionTokens` | 출력 토큰 수 |
|
|
261
|
+
| `totalTokens` | 총 토큰 수 |
|
|
262
|
+
| `costUsd` | 예상 비용 (USD) |
|
|
263
|
+
| `timestamp` | 요청 시간 |
|
|
264
|
+
| `latencyMs` | 응답 소요 시간 |
|
|
265
|
+
| `success` | 성공/실패 여부 |
|
|
266
|
+
| `error` | 에러 메시지 (실패 시) |
|
|
267
|
+
|
|
268
|
+
## 라이센스
|
|
269
|
+
|
|
270
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# LLM Tracker SDK
|
|
2
|
+
|
|
3
|
+
A backend SDK for tracking LLM API usage by team and project. Monitors token consumption, costs, and API calls for OpenAI, Claude, Gemini, Azure OpenAI, and AWS Bedrock.
|
|
4
|
+
|
|
5
|
+
> **Purpose**: Created to track and analyze LLM usage by team or project within a company. Use it for cost management and understanding API consumption patterns.
|
|
6
|
+
|
|
7
|
+
> **Note**: This SDK is **server-side only**. It requires Node.js and cannot run in browsers.
|
|
8
|
+
|
|
9
|
+
## Key Features
|
|
10
|
+
|
|
11
|
+
- **Multi-Provider Support**: OpenAI, Claude, Gemini, Azure OpenAI, AWS Bedrock
|
|
12
|
+
- **Automatic Cost Calculation**: Fetches real-time pricing from LiteLLM (refreshed every 24 hours)
|
|
13
|
+
- **Multiple DB Adapters**: PostgreSQL, MySQL, MongoDB, TypeORM
|
|
14
|
+
- **Streaming Support**: Streaming with token tracking for OpenAI, Claude, Azure
|
|
15
|
+
- **Batch Processing**: Queues tracking data for batch saving
|
|
16
|
+
- **Retry Logic**: Automatic retry with exponential backoff
|
|
17
|
+
- **Usage Statistics**: Query statistics by project, provider, model, and date range
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install llm-tracker-sdk
|
|
23
|
+
|
|
24
|
+
# Install the DB driver you'll use
|
|
25
|
+
npm install pg # PostgreSQL
|
|
26
|
+
npm install mysql2 # MySQL
|
|
27
|
+
npm install mongodb # MongoDB
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { LLMTrackerSDK, PostgresAdapter } from 'llm-tracker-sdk';
|
|
34
|
+
|
|
35
|
+
const sdk = new LLMTrackerSDK({
|
|
36
|
+
projectName: 'my-project',
|
|
37
|
+
apiKeys: {
|
|
38
|
+
openai: process.env.OPENAI_API_KEY,
|
|
39
|
+
claude: process.env.ANTHROPIC_API_KEY,
|
|
40
|
+
},
|
|
41
|
+
database: new PostgresAdapter({
|
|
42
|
+
connectionString: process.env.DATABASE_URL,
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
await sdk.connect();
|
|
47
|
+
|
|
48
|
+
const response = await sdk.openai.chat.completions.create({
|
|
49
|
+
model: 'gpt-4o',
|
|
50
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await sdk.disconnect();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Providers
|
|
57
|
+
|
|
58
|
+
### OpenAI
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
const response = await sdk.openai.chat.completions.create({
|
|
62
|
+
model: 'gpt-4o',
|
|
63
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
64
|
+
response_format: { type: 'json_object' },
|
|
65
|
+
tools: [{ type: 'function', function: { name: 'get_weather', ... } }],
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Claude
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
const response = await sdk.claude.messages.create({
|
|
73
|
+
model: 'claude-sonnet-4-20250514',
|
|
74
|
+
max_tokens: 1024,
|
|
75
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Azure OpenAI
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const sdk = new LLMTrackerSDK({
|
|
83
|
+
projectName: 'my-project',
|
|
84
|
+
apiKeys: {
|
|
85
|
+
azure: {
|
|
86
|
+
apiKey: process.env.AZURE_OPENAI_API_KEY,
|
|
87
|
+
endpoint: process.env.AZURE_OPENAI_ENDPOINT,
|
|
88
|
+
deployment: 'gpt-4o',
|
|
89
|
+
apiVersion: '2024-02-15-preview',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
database: new PostgresAdapter({ ... }),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const response = await sdk.azure.chat.completions.create({
|
|
96
|
+
model: 'gpt-4o',
|
|
97
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### AWS Bedrock
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
const sdk = new LLMTrackerSDK({
|
|
105
|
+
projectName: 'my-project',
|
|
106
|
+
apiKeys: {
|
|
107
|
+
bedrock: {
|
|
108
|
+
region: 'us-east-1',
|
|
109
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
110
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
database: new PostgresAdapter({ ... }),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const response = await sdk.bedrock.chat({
|
|
117
|
+
modelId: 'anthropic.claude-3-5-sonnet-20241022-v2:0',
|
|
118
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
119
|
+
system: 'You are a helpful assistant.',
|
|
120
|
+
maxTokens: 1024,
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Gemini
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const response = await sdk.gemini.generateContent({
|
|
128
|
+
model: 'gemini-1.5-flash',
|
|
129
|
+
contents: [{ role: 'user', parts: [{ text: 'Hello!' }] }],
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Streaming
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const stream = sdk.openai.chat.completions.stream({
|
|
137
|
+
model: 'gpt-4o',
|
|
138
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
for await (const chunk of stream) {
|
|
142
|
+
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## DB Adapters
|
|
147
|
+
|
|
148
|
+
### PostgreSQL
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { PostgresAdapter } from 'llm-tracker-sdk';
|
|
152
|
+
|
|
153
|
+
new PostgresAdapter({
|
|
154
|
+
connectionString: 'postgres://user:pass@localhost:5432/db',
|
|
155
|
+
tableName: 'llm_tracking',
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### MySQL
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { MySQLAdapter } from 'llm-tracker-sdk';
|
|
163
|
+
|
|
164
|
+
new MySQLAdapter({
|
|
165
|
+
host: 'localhost',
|
|
166
|
+
port: 3306,
|
|
167
|
+
database: 'mydb',
|
|
168
|
+
user: 'root',
|
|
169
|
+
password: 'password',
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### MongoDB
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { MongoAdapter } from 'llm-tracker-sdk';
|
|
177
|
+
|
|
178
|
+
new MongoAdapter({
|
|
179
|
+
uri: 'mongodb://localhost:27017',
|
|
180
|
+
database: 'llm_tracking',
|
|
181
|
+
collection: 'api_usage',
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Custom Adapter
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { DatabaseAdapter, TrackingData } from 'llm-tracker-sdk';
|
|
189
|
+
|
|
190
|
+
class MyAdapter implements DatabaseAdapter {
|
|
191
|
+
async connect(): Promise<void> { /* ... */ }
|
|
192
|
+
async disconnect(): Promise<void> { /* ... */ }
|
|
193
|
+
async save(data: TrackingData): Promise<void> { /* ... */ }
|
|
194
|
+
async saveBatch?(data: TrackingData[]): Promise<void> { /* ... */ }
|
|
195
|
+
async getStats?(filter: StatsFilter): Promise<UsageStats> { /* ... */ }
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Configuration
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const sdk = new LLMTrackerSDK({
|
|
203
|
+
projectName: 'my-project',
|
|
204
|
+
apiKeys: { ... },
|
|
205
|
+
database: new PostgresAdapter({ ... }),
|
|
206
|
+
|
|
207
|
+
// Custom pricing (overrides LiteLLM pricing)
|
|
208
|
+
customPricing: {
|
|
209
|
+
'my-custom-model': { inputCostPerToken: 0.00001, outputCostPerToken: 0.00002 },
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
// Retry settings
|
|
213
|
+
retry: {
|
|
214
|
+
maxRetries: 3,
|
|
215
|
+
initialDelayMs: 1000,
|
|
216
|
+
maxDelayMs: 10000,
|
|
217
|
+
backoffMultiplier: 2,
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
// Batch processing
|
|
221
|
+
batchSize: 10,
|
|
222
|
+
batchIntervalMs: 5000,
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Usage Statistics
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
const stats = await sdk.getStats({
|
|
230
|
+
projectName: 'my-project',
|
|
231
|
+
provider: 'openai',
|
|
232
|
+
startDate: new Date('2025-01-01'),
|
|
233
|
+
endDate: new Date('2025-01-31'),
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
console.log(stats);
|
|
237
|
+
// {
|
|
238
|
+
// totalRequests: 1000, // Total number of requests
|
|
239
|
+
// successfulRequests: 995, // Number of successful requests
|
|
240
|
+
// failedRequests: 5, // Number of failed requests
|
|
241
|
+
// totalTokens: 500000, // Total tokens
|
|
242
|
+
// totalPromptTokens: 200000, // Prompt tokens
|
|
243
|
+
// totalCompletionTokens: 300000, // Completion tokens
|
|
244
|
+
// totalCostUsd: 15.50, // Total cost (USD)
|
|
245
|
+
// avgLatencyMs: 850 // Average response time (ms)
|
|
246
|
+
// }
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Stored Data
|
|
250
|
+
|
|
251
|
+
Data automatically saved for each API call:
|
|
252
|
+
|
|
253
|
+
| Field | Description |
|
|
254
|
+
|-------|-------------|
|
|
255
|
+
| `requestId` | Unique request ID |
|
|
256
|
+
| `projectName` | Project identifier |
|
|
257
|
+
| `provider` | openai, claude, gemini, azure, bedrock |
|
|
258
|
+
| `model` | Model name |
|
|
259
|
+
| `promptTokens` | Input token count |
|
|
260
|
+
| `completionTokens` | Output token count |
|
|
261
|
+
| `totalTokens` | Total token count |
|
|
262
|
+
| `costUsd` | Estimated cost (USD) |
|
|
263
|
+
| `timestamp` | Request timestamp |
|
|
264
|
+
| `latencyMs` | Response latency |
|
|
265
|
+
| `success` | Success/failure status |
|
|
266
|
+
| `error` | Error message (on failure) |
|
|
267
|
+
|
|
268
|
+
## License
|
|
269
|
+
|
|
270
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { PostgresAdapter } from './postgres';
|
|
2
|
+
export type { PostgresConfig } from './postgres';
|
|
3
|
+
export { MySQLAdapter } from './mysql';
|
|
4
|
+
export type { MySQLConfig } from './mysql';
|
|
5
|
+
export { MongoAdapter } from './mongo';
|
|
6
|
+
export type { MongoConfig } from './mongo';
|
|
7
|
+
export { TypeORMAdapter } from './typeorm';
|
|
8
|
+
export type { TypeORMConfig } from './typeorm';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TypeORMAdapter = exports.MongoAdapter = exports.MySQLAdapter = exports.PostgresAdapter = void 0;
|
|
4
|
+
var postgres_1 = require("./postgres");
|
|
5
|
+
Object.defineProperty(exports, "PostgresAdapter", { enumerable: true, get: function () { return postgres_1.PostgresAdapter; } });
|
|
6
|
+
var mysql_1 = require("./mysql");
|
|
7
|
+
Object.defineProperty(exports, "MySQLAdapter", { enumerable: true, get: function () { return mysql_1.MySQLAdapter; } });
|
|
8
|
+
var mongo_1 = require("./mongo");
|
|
9
|
+
Object.defineProperty(exports, "MongoAdapter", { enumerable: true, get: function () { return mongo_1.MongoAdapter; } });
|
|
10
|
+
var typeorm_1 = require("./typeorm");
|
|
11
|
+
Object.defineProperty(exports, "TypeORMAdapter", { enumerable: true, get: function () { return typeorm_1.TypeORMAdapter; } });
|
|
12
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWRhcHRlcnMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsdUNBQTZDO0FBQXBDLDJHQUFBLGVBQWUsT0FBQTtBQUd4QixpQ0FBdUM7QUFBOUIscUdBQUEsWUFBWSxPQUFBO0FBR3JCLGlDQUF1QztBQUE5QixxR0FBQSxZQUFZLE9BQUE7QUFHckIscUNBQTJDO0FBQWxDLHlHQUFBLGNBQWMsT0FBQSIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7IFBvc3RncmVzQWRhcHRlciB9IGZyb20gJy4vcG9zdGdyZXMnO1xuZXhwb3J0IHR5cGUgeyBQb3N0Z3Jlc0NvbmZpZyB9IGZyb20gJy4vcG9zdGdyZXMnO1xuXG5leHBvcnQgeyBNeVNRTEFkYXB0ZXIgfSBmcm9tICcuL215c3FsJztcbmV4cG9ydCB0eXBlIHsgTXlTUUxDb25maWcgfSBmcm9tICcuL215c3FsJztcblxuZXhwb3J0IHsgTW9uZ29BZGFwdGVyIH0gZnJvbSAnLi9tb25nbyc7XG5leHBvcnQgdHlwZSB7IE1vbmdvQ29uZmlnIH0gZnJvbSAnLi9tb25nbyc7XG5cbmV4cG9ydCB7IFR5cGVPUk1BZGFwdGVyIH0gZnJvbSAnLi90eXBlb3JtJztcbmV4cG9ydCB0eXBlIHsgVHlwZU9STUNvbmZpZyB9IGZyb20gJy4vdHlwZW9ybSc7XG4iXX0=
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DatabaseAdapter, TrackingData } from '../types';
|
|
2
|
+
export interface MongoConfig {
|
|
3
|
+
uri: string;
|
|
4
|
+
database: string;
|
|
5
|
+
collection?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class MongoAdapter implements DatabaseAdapter {
|
|
8
|
+
private client;
|
|
9
|
+
private collection;
|
|
10
|
+
private config;
|
|
11
|
+
constructor(config: MongoConfig);
|
|
12
|
+
connect(): Promise<void>;
|
|
13
|
+
disconnect(): Promise<void>;
|
|
14
|
+
save(data: TrackingData): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.MongoAdapter = void 0;
|
|
37
|
+
class MongoAdapter {
|
|
38
|
+
constructor(config) {
|
|
39
|
+
this.config = config;
|
|
40
|
+
}
|
|
41
|
+
async connect() {
|
|
42
|
+
const { MongoClient } = await Promise.resolve().then(() => __importStar(require('mongodb')));
|
|
43
|
+
this.client = new MongoClient(this.config.uri);
|
|
44
|
+
await this.client.connect();
|
|
45
|
+
const db = this.client.db(this.config.database);
|
|
46
|
+
this.collection = db.collection(this.config.collection ?? 'llm_tracking');
|
|
47
|
+
}
|
|
48
|
+
async disconnect() {
|
|
49
|
+
if (this.client) {
|
|
50
|
+
await this.client.close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async save(data) {
|
|
54
|
+
const collection = this.collection;
|
|
55
|
+
await collection.insertOne({
|
|
56
|
+
requestId: data.requestId,
|
|
57
|
+
projectName: data.projectName,
|
|
58
|
+
provider: data.provider,
|
|
59
|
+
model: data.model,
|
|
60
|
+
tokenUsage: data.tokenUsage,
|
|
61
|
+
costUsd: data.costUsd,
|
|
62
|
+
timestamp: data.timestamp,
|
|
63
|
+
latencyMs: data.latencyMs,
|
|
64
|
+
success: data.success,
|
|
65
|
+
error: data.error,
|
|
66
|
+
createdAt: new Date(),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.MongoAdapter = MongoAdapter;
|
|
71
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9uZ28uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWRhcHRlcnMvbW9uZ28udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBUUEsTUFBYSxZQUFZO0lBS3ZCLFlBQVksTUFBbUI7UUFDN0IsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUVELEtBQUssQ0FBQyxPQUFPO1FBQ1gsTUFBTSxFQUFFLFdBQVcsRUFBRSxHQUFHLHdEQUFhLFNBQVMsR0FBQyxDQUFDO1FBQ2hELElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxXQUFXLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMvQyxNQUFPLElBQUksQ0FBQyxNQUEyQyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBRWxFLE1BQU0sRUFBRSxHQUFJLElBQUksQ0FBQyxNQUE0QyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3ZGLElBQUksQ0FBQyxVQUFVLEdBQUksRUFBZ0QsQ0FBQyxVQUFVLENBQzVFLElBQUksQ0FBQyxNQUFNLENBQUMsVUFBVSxJQUFJLGNBQWMsQ0FDekMsQ0FBQztJQUNKLENBQUM7SUFFRCxLQUFLLENBQUMsVUFBVTtRQUNkLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2hCLE1BQU8sSUFBSSxDQUFDLE1BQXlDLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDaEUsQ0FBQztJQUNILENBQUM7SUFFRCxLQUFLLENBQUMsSUFBSSxDQUFDLElBQWtCO1FBQzNCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxVQUErRCxDQUFDO1FBQ3hGLE1BQU0sVUFBVSxDQUFDLFNBQVMsQ0FBQztZQUN6QixTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7WUFDekIsV0FBVyxFQUFFLElBQUksQ0FBQyxXQUFXO1lBQzdCLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUTtZQUN2QixLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUs7WUFDakIsVUFBVSxFQUFFLElBQUksQ0FBQyxVQUFVO1lBQzNCLE9BQU8sRUFBRSxJQUFJLENBQUMsT0FBTztZQUNyQixTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7WUFDekIsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTO1lBQ3pCLE9BQU8sRUFBRSxJQUFJLENBQUMsT0FBTztZQUNyQixLQUFLLEVBQUUsSUFBSSxDQUFDLEtBQUs7WUFDakIsU0FBUyxFQUFFLElBQUksSUFBSSxFQUFFO1NBQ3RCLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRjtBQTFDRCxvQ0EwQ0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IERhdGFiYXNlQWRhcHRlciwgVHJhY2tpbmdEYXRhIH0gZnJvbSAnLi4vdHlwZXMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE1vbmdvQ29uZmlnIHtcbiAgdXJpOiBzdHJpbmc7XG4gIGRhdGFiYXNlOiBzdHJpbmc7XG4gIGNvbGxlY3Rpb24/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBjbGFzcyBNb25nb0FkYXB0ZXIgaW1wbGVtZW50cyBEYXRhYmFzZUFkYXB0ZXIge1xuICBwcml2YXRlIGNsaWVudDogdW5rbm93bjtcbiAgcHJpdmF0ZSBjb2xsZWN0aW9uOiB1bmtub3duO1xuICBwcml2YXRlIGNvbmZpZzogTW9uZ29Db25maWc7XG5cbiAgY29uc3RydWN0b3IoY29uZmlnOiBNb25nb0NvbmZpZykge1xuICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICB9XG5cbiAgYXN5bmMgY29ubmVjdCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IE1vbmdvQ2xpZW50IH0gPSBhd2FpdCBpbXBvcnQoJ21vbmdvZGInKTtcbiAgICB0aGlzLmNsaWVudCA9IG5ldyBNb25nb0NsaWVudCh0aGlzLmNvbmZpZy51cmkpO1xuICAgIGF3YWl0ICh0aGlzLmNsaWVudCBhcyB7IGNvbm5lY3Q6ICgpID0+IFByb21pc2U8dm9pZD4gfSkuY29ubmVjdCgpO1xuXG4gICAgY29uc3QgZGIgPSAodGhpcy5jbGllbnQgYXMgeyBkYjogKG5hbWU6IHN0cmluZykgPT4gdW5rbm93biB9KS5kYih0aGlzLmNvbmZpZy5kYXRhYmFzZSk7XG4gICAgdGhpcy5jb2xsZWN0aW9uID0gKGRiIGFzIHsgY29sbGVjdGlvbjogKG5hbWU6IHN0cmluZykgPT4gdW5rbm93biB9KS5jb2xsZWN0aW9uKFxuICAgICAgdGhpcy5jb25maWcuY29sbGVjdGlvbiA/PyAnbGxtX3RyYWNraW5nJ1xuICAgICk7XG4gIH1cblxuICBhc3luYyBkaXNjb25uZWN0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLmNsaWVudCkge1xuICAgICAgYXdhaXQgKHRoaXMuY2xpZW50IGFzIHsgY2xvc2U6ICgpID0+IFByb21pc2U8dm9pZD4gfSkuY2xvc2UoKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBzYXZlKGRhdGE6IFRyYWNraW5nRGF0YSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGNvbGxlY3Rpb24gPSB0aGlzLmNvbGxlY3Rpb24gYXMgeyBpbnNlcnRPbmU6IChkb2M6IHVua25vd24pID0+IFByb21pc2U8dW5rbm93bj4gfTtcbiAgICBhd2FpdCBjb2xsZWN0aW9uLmluc2VydE9uZSh7XG4gICAgICByZXF1ZXN0SWQ6IGRhdGEucmVxdWVzdElkLFxuICAgICAgcHJvamVjdE5hbWU6IGRhdGEucHJvamVjdE5hbWUsXG4gICAgICBwcm92aWRlcjogZGF0YS5wcm92aWRlcixcbiAgICAgIG1vZGVsOiBkYXRhLm1vZGVsLFxuICAgICAgdG9rZW5Vc2FnZTogZGF0YS50b2tlblVzYWdlLFxuICAgICAgY29zdFVzZDogZGF0YS5jb3N0VXNkLFxuICAgICAgdGltZXN0YW1wOiBkYXRhLnRpbWVzdGFtcCxcbiAgICAgIGxhdGVuY3lNczogZGF0YS5sYXRlbmN5TXMsXG4gICAgICBzdWNjZXNzOiBkYXRhLnN1Y2Nlc3MsXG4gICAgICBlcnJvcjogZGF0YS5lcnJvcixcbiAgICAgIGNyZWF0ZWRBdDogbmV3IERhdGUoKSxcbiAgICB9KTtcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { DatabaseAdapter, TrackingData } from '../types';
|
|
2
|
+
export interface MySQLConfig {
|
|
3
|
+
host: string;
|
|
4
|
+
port?: number;
|
|
5
|
+
database: string;
|
|
6
|
+
user: string;
|
|
7
|
+
password: string;
|
|
8
|
+
tableName?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare class MySQLAdapter implements DatabaseAdapter {
|
|
11
|
+
private pool;
|
|
12
|
+
private tableName;
|
|
13
|
+
private config;
|
|
14
|
+
constructor(config: MySQLConfig);
|
|
15
|
+
connect(): Promise<void>;
|
|
16
|
+
disconnect(): Promise<void>;
|
|
17
|
+
save(data: TrackingData): Promise<void>;
|
|
18
|
+
}
|