@theihtisham/budget-llm 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/.env.example +21 -0
- package/LICENSE +21 -0
- package/README.md +293 -0
- package/dist/config.d.ts +77 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +246 -0
- package/dist/config.js.map +1 -0
- package/dist/database.d.ts +24 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +414 -0
- package/dist/database.js.map +1 -0
- package/dist/providers.d.ts +20 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +208 -0
- package/dist/providers.js.map +1 -0
- package/dist/proxy.d.ts +7 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +181 -0
- package/dist/proxy.js.map +1 -0
- package/dist/rate-limiter.d.ts +8 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +72 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/router.d.ts +33 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +186 -0
- package/dist/router.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +705 -0
- package/dist/server.js.map +1 -0
- package/dist/task-classifier.d.ts +4 -0
- package/dist/task-classifier.d.ts.map +1 -0
- package/dist/task-classifier.js +123 -0
- package/dist/task-classifier.js.map +1 -0
- package/dist/types.d.ts +205 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +46 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/encryption.d.ts +4 -0
- package/dist/utils/encryption.d.ts.map +1 -0
- package/dist/utils/encryption.js +40 -0
- package/dist/utils/encryption.js.map +1 -0
- package/package.json +63 -0
- package/src/config.ts +254 -0
- package/src/database.ts +496 -0
- package/src/providers.ts +315 -0
- package/src/proxy.ts +226 -0
- package/src/rate-limiter.ts +81 -0
- package/src/router.ts +228 -0
- package/src/server.ts +754 -0
- package/src/task-classifier.ts +134 -0
- package/src/types/sql.js.d.ts +27 -0
- package/src/types.ts +258 -0
- package/src/utils/encryption.ts +36 -0
- package/tests/config.test.ts +85 -0
- package/tests/database.test.ts +194 -0
- package/tests/encryption.test.ts +57 -0
- package/tests/rate-limiter.test.ts +83 -0
- package/tests/router.test.ts +182 -0
- package/tests/server.test.ts +253 -0
- package/tests/setup.ts +15 -0
- package/tests/task-classifier.test.ts +117 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +15 -0
package/.env.example
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Server
|
|
2
|
+
PORT=3210
|
|
3
|
+
NODE_ENV=development
|
|
4
|
+
|
|
5
|
+
# Encryption key for API key storage (generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")
|
|
6
|
+
ENCRYPTION_KEY=change_me_to_a_64_char_hex_string_in_production_at_least_32_bytes_here
|
|
7
|
+
|
|
8
|
+
# Default budget limits (in USD)
|
|
9
|
+
DEFAULT_DAILY_BUDGET=10.00
|
|
10
|
+
DEFAULT_MONTHLY_BUDGET=200.00
|
|
11
|
+
DEFAULT_PER_REQUEST_CAP=1.00
|
|
12
|
+
|
|
13
|
+
# Cache TTL in seconds
|
|
14
|
+
CACHE_TTL=3600
|
|
15
|
+
|
|
16
|
+
# Rate limiting
|
|
17
|
+
RATE_LIMIT_WINDOW_MS=60000
|
|
18
|
+
RATE_LIMIT_MAX_REQUESTS=60
|
|
19
|
+
|
|
20
|
+
# Logging level: debug, info, warn, error
|
|
21
|
+
LOG_LEVEL=info
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 BudgetLLM Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# BudgetLLM
|
|
2
|
+
|
|
3
|
+
> Cut your AI costs by 60% — one API endpoint that automatically picks the cheapest model for every request.
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://vitest.dev/)
|
|
9
|
+
|
|
10
|
+
BudgetLLM is a smart multi-provider LLM cost optimizer and routing proxy. It sits between your application and LLM providers (OpenAI, Anthropic, Google, DeepSeek) and automatically routes each request to the cheapest capable model based on the task type.
|
|
11
|
+
|
|
12
|
+
## How It Works
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Your App
|
|
16
|
+
|
|
|
17
|
+
| POST /v1/chat/completions (OpenAI-compatible)
|
|
18
|
+
v
|
|
19
|
+
+------------------+
|
|
20
|
+
| BudgetLLM |
|
|
21
|
+
| Proxy Server |
|
|
22
|
+
+------------------+
|
|
23
|
+
| | | |
|
|
24
|
+
| Task | Budget | Cache | Rate Limit
|
|
25
|
+
| Classification | Enforcement | Lookup | Check
|
|
26
|
+
| | | |
|
|
27
|
+
v v v v
|
|
28
|
+
+----------------------------------------------------------+
|
|
29
|
+
| Smart Router |
|
|
30
|
+
| code tasks -> cheapest capable model |
|
|
31
|
+
| creative tasks -> highest quality model |
|
|
32
|
+
| chat tasks -> fastest cheapest model |
|
|
33
|
+
| reasoning -> best reasoning model |
|
|
34
|
+
+----------------------------------------------------------+
|
|
35
|
+
| | | |
|
|
36
|
+
v v v v
|
|
37
|
+
+--------+ +--------+ +--------+ +--------+
|
|
38
|
+
| OpenAI | |Anthropic| | Google | |DeepSeek|
|
|
39
|
+
+--------+ +--------+ +--------+ +--------+
|
|
40
|
+
| | | |
|
|
41
|
+
+-------------+------+------+-------------+
|
|
42
|
+
|
|
|
43
|
+
Cost Tracking
|
|
44
|
+
& Response Cache
|
|
45
|
+
|
|
|
46
|
+
v
|
|
47
|
+
SQLite Database
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
- **Smart Routing** - Automatically classifies tasks (code, creative, reasoning, chat, etc.) and picks the cheapest model that can handle it well
|
|
53
|
+
- **Budget Limits** - Daily, monthly, and per-request cost caps with automatic enforcement
|
|
54
|
+
- **Fallback Chain** - If the primary provider fails, automatically tries the next best provider
|
|
55
|
+
- **Cost Tracking** - Per-request, per-day, per-model cost logging with SQLite storage
|
|
56
|
+
- **Response Caching** - Identical prompts return cached responses (free!) with configurable TTL
|
|
57
|
+
- **Rate Limiting** - Token bucket algorithm with per-IP limits
|
|
58
|
+
- **Real-time Dashboard** - Beautiful web UI showing costs, savings, usage, and budget status
|
|
59
|
+
- **OpenAI-Compatible** - Drop-in replacement for the OpenAI API. Just change the base URL.
|
|
60
|
+
- **Security** - API key encryption (AES-256-GCM), rate limiting, input validation, no prompt content logging
|
|
61
|
+
|
|
62
|
+
## Cost Comparison
|
|
63
|
+
|
|
64
|
+
BudgetLLM automatically picks the cheapest model for each task type. Here's how costs compare:
|
|
65
|
+
|
|
66
|
+
| Task Type | Default Route | Cost/1M Tokens | vs GPT-4 Turbo | Savings |
|
|
67
|
+
|-----------|--------------|----------------|-----------------|---------|
|
|
68
|
+
| Chat | Gemini 2.0 Flash | $0.10 / $0.40 | $10 / $30 | **97%** |
|
|
69
|
+
| Code | GPT-4o Mini | $0.15 / $0.60 | $10 / $30 | **98%** |
|
|
70
|
+
| Creative | GPT-4o | $2.50 / $10.00 | $10 / $30 | **67%** |
|
|
71
|
+
| Reasoning | DeepSeek R1 | $0.55 / $2.19 | $10 / $30 | **93%** |
|
|
72
|
+
| Summarization | Gemini 2.0 Flash | $0.10 / $0.40 | $10 / $30 | **97%** |
|
|
73
|
+
| Translation | Gemini 2.0 Flash Lite | $0.075 / $0.30 | $10 / $30 | **99%** |
|
|
74
|
+
| Math | DeepSeek R1 | $0.55 / $2.19 | $10 / $30 | **93%** |
|
|
75
|
+
| Analysis | DeepSeek V3 | $0.27 / $1.10 | $10 / $30 | **96%** |
|
|
76
|
+
|
|
77
|
+
### Model Catalog
|
|
78
|
+
|
|
79
|
+
| Model | Provider | Input/1M | Output/1M | Quality | Speed | Cost |
|
|
80
|
+
|-------|----------|----------|-----------|---------|-------|------|
|
|
81
|
+
| GPT-4o | OpenAI | $2.50 | $10.00 | 9/10 | 7/10 | 5/10 |
|
|
82
|
+
| GPT-4o Mini | OpenAI | $0.15 | $0.60 | 7/10 | 9/10 | 9/10 |
|
|
83
|
+
| GPT-4 Turbo | OpenAI | $10.00 | $30.00 | 9/10 | 6/10 | 3/10 |
|
|
84
|
+
| Claude Sonnet 4 | Anthropic | $3.00 | $15.00 | 9/10 | 7/10 | 5/10 |
|
|
85
|
+
| Claude 3.5 Haiku | Anthropic | $0.80 | $4.00 | 7/10 | 9/10 | 7/10 |
|
|
86
|
+
| Gemini 2.0 Flash | Google | $0.10 | $0.40 | 7/10 | 10/10 | 10/10 |
|
|
87
|
+
| Gemini 2.0 Flash Lite | Google | $0.075 | $0.30 | 6/10 | 10/10 | 10/10 |
|
|
88
|
+
| DeepSeek V3 | DeepSeek | $0.27 | $1.10 | 8/10 | 8/10 | 8/10 |
|
|
89
|
+
| DeepSeek R1 | DeepSeek | $0.55 | $2.19 | 9/10 | 5/10 | 7/10 |
|
|
90
|
+
|
|
91
|
+
## Quick Start
|
|
92
|
+
|
|
93
|
+
### 1. Install
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
git clone https://github.com/your-username/budget-llm.git
|
|
97
|
+
cd budget-llm
|
|
98
|
+
npm install
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 2. Configure
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
cp .env.example .env
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Edit `.env` and add your API keys:
|
|
108
|
+
|
|
109
|
+
```env
|
|
110
|
+
# Add at least one provider API key
|
|
111
|
+
OPENAI_API_KEY=sk-...
|
|
112
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
113
|
+
GOOGLE_API_KEY=AIza...
|
|
114
|
+
DEEPSEEK_API_KEY=sk-...
|
|
115
|
+
|
|
116
|
+
# Set your budget limits
|
|
117
|
+
DEFAULT_DAILY_BUDGET=10.00
|
|
118
|
+
DEFAULT_MONTHLY_BUDGET=200.00
|
|
119
|
+
DEFAULT_PER_REQUEST_CAP=1.00
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 3. Run
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Development
|
|
126
|
+
npm run dev
|
|
127
|
+
|
|
128
|
+
# Production
|
|
129
|
+
npm run build
|
|
130
|
+
npm start
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 4. Use It
|
|
134
|
+
|
|
135
|
+
BudgetLLM is a drop-in replacement for the OpenAI API. Just change the base URL:
|
|
136
|
+
|
|
137
|
+
**Before (direct OpenAI):**
|
|
138
|
+
```javascript
|
|
139
|
+
const openai = new OpenAI({
|
|
140
|
+
apiKey: 'sk-your-key',
|
|
141
|
+
baseURL: 'https://api.openai.com/v1',
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**After (via BudgetLLM):**
|
|
146
|
+
```javascript
|
|
147
|
+
const openai = new OpenAI({
|
|
148
|
+
apiKey: 'anything', // BudgetLLM doesn't require a client key
|
|
149
|
+
baseURL: 'http://localhost:3210/v1',
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
That's it. Every request is now automatically optimized for cost.
|
|
154
|
+
|
|
155
|
+
### 5. Optional: Task Type Hints
|
|
156
|
+
|
|
157
|
+
Help BudgetLLM pick the best model by specifying a task type:
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
const response = await openai.chat.completions.create({
|
|
161
|
+
model: 'auto', // let BudgetLLM decide
|
|
162
|
+
messages: [{ role: 'user', content: 'Write a sort function' }],
|
|
163
|
+
// BudgetLLM extension:
|
|
164
|
+
task_type: 'code', // forces code-optimized routing
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## API Reference
|
|
169
|
+
|
|
170
|
+
### OpenAI-Compatible Endpoints
|
|
171
|
+
|
|
172
|
+
| Method | Endpoint | Description |
|
|
173
|
+
|--------|----------|-------------|
|
|
174
|
+
| POST | `/v1/chat/completions` | Chat completion (drop-in replacement) |
|
|
175
|
+
| GET | `/v1/models` | List available models |
|
|
176
|
+
|
|
177
|
+
### BudgetLLM Extensions
|
|
178
|
+
|
|
179
|
+
The `/v1/chat/completions` endpoint accepts these additional fields:
|
|
180
|
+
|
|
181
|
+
| Field | Type | Description |
|
|
182
|
+
|-------|------|-------------|
|
|
183
|
+
| `task_type` | string | Force routing: `code`, `creative`, `reasoning`, `chat`, `summarization`, `translation`, `analysis`, `math`, `auto` |
|
|
184
|
+
| `request_id` | string | Custom request ID for tracking |
|
|
185
|
+
| `budget_cap` | number | Per-request budget cap in USD |
|
|
186
|
+
|
|
187
|
+
The response includes a `cost` field:
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"cost": {
|
|
192
|
+
"inputCost": 0.0000015,
|
|
193
|
+
"outputCost": 0.000006,
|
|
194
|
+
"totalCost": 0.0000075,
|
|
195
|
+
"currency": "USD",
|
|
196
|
+
"model": "gpt-4o-mini",
|
|
197
|
+
"provider": "openai",
|
|
198
|
+
"savingsVsGpt4": 0.00012
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Management API
|
|
204
|
+
|
|
205
|
+
| Method | Endpoint | Description |
|
|
206
|
+
|--------|----------|-------------|
|
|
207
|
+
| GET | `/health` | Health check |
|
|
208
|
+
| GET | `/api/dashboard` | Full dashboard data |
|
|
209
|
+
| GET | `/api/costs?days=30` | Cost summary |
|
|
210
|
+
| GET | `/api/budget` | Budget config and status |
|
|
211
|
+
| PUT | `/api/budget` | Update budget limits |
|
|
212
|
+
| GET | `/api/rate-limit` | Rate limit status |
|
|
213
|
+
| DELETE | `/api/cache` | Clear response cache |
|
|
214
|
+
| GET | `/dashboard` | Web dashboard UI |
|
|
215
|
+
|
|
216
|
+
## Architecture
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
src/
|
|
220
|
+
server.ts # Express HTTP server with all routes
|
|
221
|
+
proxy.ts # Main request handler (orchestrates everything)
|
|
222
|
+
router.ts # Smart model routing engine
|
|
223
|
+
task-classifier.ts # Task type detection from prompts
|
|
224
|
+
providers.ts # Provider adapters (OpenAI, Anthropic, Google, DeepSeek)
|
|
225
|
+
database.ts # SQLite database layer
|
|
226
|
+
rate-limiter.ts # Token bucket rate limiter
|
|
227
|
+
config.ts # Configuration, model catalog, logging
|
|
228
|
+
types.ts # TypeScript type definitions
|
|
229
|
+
utils/
|
|
230
|
+
encryption.ts # AES-256-GCM encryption for API keys
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Routing Algorithm
|
|
234
|
+
|
|
235
|
+
BudgetLLM scores each model using weighted criteria that vary by task type:
|
|
236
|
+
|
|
237
|
+
| Task Type | Quality Weight | Cost Weight | Speed Weight |
|
|
238
|
+
|-----------|---------------|-------------|--------------|
|
|
239
|
+
| Code | 40% | 45% | 15% |
|
|
240
|
+
| Creative | 70% | 15% | 15% |
|
|
241
|
+
| Reasoning | 65% | 25% | 10% |
|
|
242
|
+
| Chat | 20% | 60% | 20% |
|
|
243
|
+
| Summarization | 20% | 60% | 20% |
|
|
244
|
+
| Translation | 20% | 60% | 20% |
|
|
245
|
+
| Analysis | 50% | 35% | 15% |
|
|
246
|
+
|
|
247
|
+
Models also get a +20 score bonus if they declare the task type as a capability, and a -10 penalty otherwise.
|
|
248
|
+
|
|
249
|
+
### Fallback Chain
|
|
250
|
+
|
|
251
|
+
When the primary provider fails, BudgetLLM tries up to 3 alternative providers in priority order. This means your app stays up even if one provider has an outage.
|
|
252
|
+
|
|
253
|
+
### Caching
|
|
254
|
+
|
|
255
|
+
Identical prompts (same messages, model, temperature, max_tokens) are cached with a configurable TTL. Cached responses are served instantly with zero cost. The cache uses content-hash matching so even rephrased requests that happen to match the exact same parameters get the benefit.
|
|
256
|
+
|
|
257
|
+
## Security
|
|
258
|
+
|
|
259
|
+
- **API Key Encryption** - Stored keys are encrypted with AES-256-GCM
|
|
260
|
+
- **Rate Limiting** - Token bucket algorithm prevents abuse
|
|
261
|
+
- **Input Validation** - Zod schema validation on all inputs
|
|
262
|
+
- **No Prompt Logging** - Request metadata is logged, but prompt content is never stored in logs
|
|
263
|
+
- **Helmet** - HTTP security headers via helmet middleware
|
|
264
|
+
- **Budget Enforcement** - Hard limits prevent runaway spending
|
|
265
|
+
|
|
266
|
+
## Development
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Install dependencies
|
|
270
|
+
npm install
|
|
271
|
+
|
|
272
|
+
# Run in development mode with hot reload
|
|
273
|
+
npm run dev
|
|
274
|
+
|
|
275
|
+
# Run tests
|
|
276
|
+
npm test
|
|
277
|
+
|
|
278
|
+
# Run tests in watch mode
|
|
279
|
+
npm run test:watch
|
|
280
|
+
|
|
281
|
+
# Generate coverage report
|
|
282
|
+
npm run test:coverage
|
|
283
|
+
|
|
284
|
+
# Type check
|
|
285
|
+
npm run lint
|
|
286
|
+
|
|
287
|
+
# Build for production
|
|
288
|
+
npm run build
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## License
|
|
292
|
+
|
|
293
|
+
[MIT](LICENSE)
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ProviderConfig, ModelInfo, BudgetConfig } from './types';
|
|
3
|
+
declare const envSchema: z.ZodObject<{
|
|
4
|
+
PORT: z.ZodDefault<z.ZodNumber>;
|
|
5
|
+
NODE_ENV: z.ZodDefault<z.ZodEnum<["development", "production", "test"]>>;
|
|
6
|
+
ENCRYPTION_KEY: z.ZodDefault<z.ZodString>;
|
|
7
|
+
DEFAULT_DAILY_BUDGET: z.ZodDefault<z.ZodNumber>;
|
|
8
|
+
DEFAULT_MONTHLY_BUDGET: z.ZodDefault<z.ZodNumber>;
|
|
9
|
+
DEFAULT_PER_REQUEST_CAP: z.ZodDefault<z.ZodNumber>;
|
|
10
|
+
CACHE_TTL: z.ZodDefault<z.ZodNumber>;
|
|
11
|
+
RATE_LIMIT_WINDOW_MS: z.ZodDefault<z.ZodNumber>;
|
|
12
|
+
RATE_LIMIT_MAX_REQUESTS: z.ZodDefault<z.ZodNumber>;
|
|
13
|
+
LOG_LEVEL: z.ZodDefault<z.ZodEnum<["debug", "info", "warn", "error"]>>;
|
|
14
|
+
OPENAI_API_KEY: z.ZodOptional<z.ZodString>;
|
|
15
|
+
ANTHROPIC_API_KEY: z.ZodOptional<z.ZodString>;
|
|
16
|
+
GOOGLE_API_KEY: z.ZodOptional<z.ZodString>;
|
|
17
|
+
DEEPSEEK_API_KEY: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, "strip", z.ZodTypeAny, {
|
|
19
|
+
PORT: number;
|
|
20
|
+
NODE_ENV: "development" | "production" | "test";
|
|
21
|
+
ENCRYPTION_KEY: string;
|
|
22
|
+
DEFAULT_DAILY_BUDGET: number;
|
|
23
|
+
DEFAULT_MONTHLY_BUDGET: number;
|
|
24
|
+
DEFAULT_PER_REQUEST_CAP: number;
|
|
25
|
+
CACHE_TTL: number;
|
|
26
|
+
RATE_LIMIT_WINDOW_MS: number;
|
|
27
|
+
RATE_LIMIT_MAX_REQUESTS: number;
|
|
28
|
+
LOG_LEVEL: "debug" | "info" | "warn" | "error";
|
|
29
|
+
OPENAI_API_KEY?: string | undefined;
|
|
30
|
+
ANTHROPIC_API_KEY?: string | undefined;
|
|
31
|
+
GOOGLE_API_KEY?: string | undefined;
|
|
32
|
+
DEEPSEEK_API_KEY?: string | undefined;
|
|
33
|
+
}, {
|
|
34
|
+
PORT?: number | undefined;
|
|
35
|
+
NODE_ENV?: "development" | "production" | "test" | undefined;
|
|
36
|
+
ENCRYPTION_KEY?: string | undefined;
|
|
37
|
+
DEFAULT_DAILY_BUDGET?: number | undefined;
|
|
38
|
+
DEFAULT_MONTHLY_BUDGET?: number | undefined;
|
|
39
|
+
DEFAULT_PER_REQUEST_CAP?: number | undefined;
|
|
40
|
+
CACHE_TTL?: number | undefined;
|
|
41
|
+
RATE_LIMIT_WINDOW_MS?: number | undefined;
|
|
42
|
+
RATE_LIMIT_MAX_REQUESTS?: number | undefined;
|
|
43
|
+
LOG_LEVEL?: "debug" | "info" | "warn" | "error" | undefined;
|
|
44
|
+
OPENAI_API_KEY?: string | undefined;
|
|
45
|
+
ANTHROPIC_API_KEY?: string | undefined;
|
|
46
|
+
GOOGLE_API_KEY?: string | undefined;
|
|
47
|
+
DEEPSEEK_API_KEY?: string | undefined;
|
|
48
|
+
}>;
|
|
49
|
+
export type EnvConfig = z.infer<typeof envSchema>;
|
|
50
|
+
export declare const env: {
|
|
51
|
+
PORT: number;
|
|
52
|
+
NODE_ENV: "development" | "production" | "test";
|
|
53
|
+
ENCRYPTION_KEY: string;
|
|
54
|
+
DEFAULT_DAILY_BUDGET: number;
|
|
55
|
+
DEFAULT_MONTHLY_BUDGET: number;
|
|
56
|
+
DEFAULT_PER_REQUEST_CAP: number;
|
|
57
|
+
CACHE_TTL: number;
|
|
58
|
+
RATE_LIMIT_WINDOW_MS: number;
|
|
59
|
+
RATE_LIMIT_MAX_REQUESTS: number;
|
|
60
|
+
LOG_LEVEL: "debug" | "info" | "warn" | "error";
|
|
61
|
+
OPENAI_API_KEY?: string | undefined;
|
|
62
|
+
ANTHROPIC_API_KEY?: string | undefined;
|
|
63
|
+
GOOGLE_API_KEY?: string | undefined;
|
|
64
|
+
DEEPSEEK_API_KEY?: string | undefined;
|
|
65
|
+
};
|
|
66
|
+
export declare function getProviders(): ProviderConfig[];
|
|
67
|
+
export declare const MODEL_CATALOG: ModelInfo[];
|
|
68
|
+
export declare function getDefaultBudget(): BudgetConfig;
|
|
69
|
+
export declare function getDbPath(): string;
|
|
70
|
+
export declare const log: {
|
|
71
|
+
debug: (msg: string, data?: unknown) => void;
|
|
72
|
+
info: (msg: string, data?: unknown) => void;
|
|
73
|
+
warn: (msg: string, data?: unknown) => void;
|
|
74
|
+
error: (msg: string, data?: unknown) => void;
|
|
75
|
+
};
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAMvE,QAAA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAeb,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AAalD,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;CAAa,CAAC;AAI9B,wBAAgB,YAAY,IAAI,cAAc,EAAE,CAuC/C;AAID,eAAO,MAAM,aAAa,EAAE,SAAS,EA0HpC,CAAC;AAIF,wBAAgB,gBAAgB,IAAI,YAAY,CAM/C;AAID,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAgBD,eAAO,MAAM,GAAG;iBACD,MAAM,SAAS,OAAO;gBAGvB,MAAM,SAAS,OAAO;gBAGtB,MAAM,SAAS,OAAO;iBAGrB,MAAM,SAAS,OAAO;CAGpC,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.log = exports.MODEL_CATALOG = exports.env = void 0;
|
|
7
|
+
exports.getProviders = getProviders;
|
|
8
|
+
exports.getDefaultBudget = getDefaultBudget;
|
|
9
|
+
exports.getDbPath = getDbPath;
|
|
10
|
+
const zod_1 = require("zod");
|
|
11
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
dotenv_1.default.config({ path: path_1.default.resolve(process.cwd(), '.env') });
|
|
14
|
+
// ---- Environment Schema ----
|
|
15
|
+
const envSchema = zod_1.z.object({
|
|
16
|
+
PORT: zod_1.z.coerce.number().default(3210),
|
|
17
|
+
NODE_ENV: zod_1.z.enum(['development', 'production', 'test']).default('development'),
|
|
18
|
+
ENCRYPTION_KEY: zod_1.z.string().min(32).default('dev_key_change_in_production_32chars!!'),
|
|
19
|
+
DEFAULT_DAILY_BUDGET: zod_1.z.coerce.number().positive().default(10),
|
|
20
|
+
DEFAULT_MONTHLY_BUDGET: zod_1.z.coerce.number().positive().default(200),
|
|
21
|
+
DEFAULT_PER_REQUEST_CAP: zod_1.z.coerce.number().positive().default(1),
|
|
22
|
+
CACHE_TTL: zod_1.z.coerce.number().positive().default(3600),
|
|
23
|
+
RATE_LIMIT_WINDOW_MS: zod_1.z.coerce.number().positive().default(60000),
|
|
24
|
+
RATE_LIMIT_MAX_REQUESTS: zod_1.z.coerce.number().positive().default(60),
|
|
25
|
+
LOG_LEVEL: zod_1.z.enum(['debug', 'info', 'warn', 'error']).default('info'),
|
|
26
|
+
OPENAI_API_KEY: zod_1.z.string().optional(),
|
|
27
|
+
ANTHROPIC_API_KEY: zod_1.z.string().optional(),
|
|
28
|
+
GOOGLE_API_KEY: zod_1.z.string().optional(),
|
|
29
|
+
DEEPSEEK_API_KEY: zod_1.z.string().optional(),
|
|
30
|
+
});
|
|
31
|
+
function parseEnv() {
|
|
32
|
+
const result = envSchema.safeParse(process.env);
|
|
33
|
+
if (!result.success) {
|
|
34
|
+
const errors = result.error.issues
|
|
35
|
+
.map((i) => `${i.path.join('.')}: ${i.message}`)
|
|
36
|
+
.join('; ');
|
|
37
|
+
throw new Error(`Configuration error: ${errors}`);
|
|
38
|
+
}
|
|
39
|
+
return result.data;
|
|
40
|
+
}
|
|
41
|
+
exports.env = parseEnv();
|
|
42
|
+
// ---- Provider Configurations ----
|
|
43
|
+
function getProviders() {
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
id: 'openai',
|
|
47
|
+
name: 'OpenAI',
|
|
48
|
+
baseUrl: 'https://api.openai.com/v1',
|
|
49
|
+
apiKey: exports.env.OPENAI_API_KEY ?? '',
|
|
50
|
+
enabled: !!exports.env.OPENAI_API_KEY,
|
|
51
|
+
priority: 1,
|
|
52
|
+
timeoutMs: 30000,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'anthropic',
|
|
56
|
+
name: 'Anthropic',
|
|
57
|
+
baseUrl: 'https://api.anthropic.com/v1',
|
|
58
|
+
apiKey: exports.env.ANTHROPIC_API_KEY ?? '',
|
|
59
|
+
enabled: !!exports.env.ANTHROPIC_API_KEY,
|
|
60
|
+
priority: 2,
|
|
61
|
+
timeoutMs: 30000,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'google',
|
|
65
|
+
name: 'Google',
|
|
66
|
+
baseUrl: 'https://generativelanguage.googleapis.com/v1beta',
|
|
67
|
+
apiKey: exports.env.GOOGLE_API_KEY ?? '',
|
|
68
|
+
enabled: !!exports.env.GOOGLE_API_KEY,
|
|
69
|
+
priority: 3,
|
|
70
|
+
timeoutMs: 30000,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'deepseek',
|
|
74
|
+
name: 'DeepSeek',
|
|
75
|
+
baseUrl: 'https://api.deepseek.com/v1',
|
|
76
|
+
apiKey: exports.env.DEEPSEEK_API_KEY ?? '',
|
|
77
|
+
enabled: !!exports.env.DEEPSEEK_API_KEY,
|
|
78
|
+
priority: 4,
|
|
79
|
+
timeoutMs: 30000,
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
// ---- Model Catalog ----
|
|
84
|
+
exports.MODEL_CATALOG = [
|
|
85
|
+
// OpenAI
|
|
86
|
+
{
|
|
87
|
+
id: 'gpt-4o',
|
|
88
|
+
provider: 'openai',
|
|
89
|
+
displayName: 'GPT-4o',
|
|
90
|
+
inputPricePer1M: 2.50,
|
|
91
|
+
outputPricePer1M: 10.00,
|
|
92
|
+
contextWindow: 128000,
|
|
93
|
+
maxOutputTokens: 16384,
|
|
94
|
+
capabilities: ['code', 'creative', 'reasoning', 'chat', 'analysis', 'math'],
|
|
95
|
+
qualityScore: 9,
|
|
96
|
+
speedScore: 7,
|
|
97
|
+
costScore: 5,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: 'gpt-4o-mini',
|
|
101
|
+
provider: 'openai',
|
|
102
|
+
displayName: 'GPT-4o Mini',
|
|
103
|
+
inputPricePer1M: 0.15,
|
|
104
|
+
outputPricePer1M: 0.60,
|
|
105
|
+
contextWindow: 128000,
|
|
106
|
+
maxOutputTokens: 16384,
|
|
107
|
+
capabilities: ['code', 'chat', 'summarization', 'translation'],
|
|
108
|
+
qualityScore: 7,
|
|
109
|
+
speedScore: 9,
|
|
110
|
+
costScore: 9,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'gpt-4-turbo',
|
|
114
|
+
provider: 'openai',
|
|
115
|
+
displayName: 'GPT-4 Turbo',
|
|
116
|
+
inputPricePer1M: 10.00,
|
|
117
|
+
outputPricePer1M: 30.00,
|
|
118
|
+
contextWindow: 128000,
|
|
119
|
+
maxOutputTokens: 4096,
|
|
120
|
+
capabilities: ['code', 'creative', 'reasoning', 'chat', 'analysis', 'math'],
|
|
121
|
+
qualityScore: 9,
|
|
122
|
+
speedScore: 6,
|
|
123
|
+
costScore: 3,
|
|
124
|
+
},
|
|
125
|
+
// Anthropic
|
|
126
|
+
{
|
|
127
|
+
id: 'claude-sonnet-4-20250514',
|
|
128
|
+
provider: 'anthropic',
|
|
129
|
+
displayName: 'Claude Sonnet 4',
|
|
130
|
+
inputPricePer1M: 3.00,
|
|
131
|
+
outputPricePer1M: 15.00,
|
|
132
|
+
contextWindow: 200000,
|
|
133
|
+
maxOutputTokens: 8192,
|
|
134
|
+
capabilities: ['code', 'creative', 'reasoning', 'chat', 'analysis'],
|
|
135
|
+
qualityScore: 9,
|
|
136
|
+
speedScore: 7,
|
|
137
|
+
costScore: 5,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'claude-haiku-3-5-20241022',
|
|
141
|
+
provider: 'anthropic',
|
|
142
|
+
displayName: 'Claude 3.5 Haiku',
|
|
143
|
+
inputPricePer1M: 0.80,
|
|
144
|
+
outputPricePer1M: 4.00,
|
|
145
|
+
contextWindow: 200000,
|
|
146
|
+
maxOutputTokens: 8192,
|
|
147
|
+
capabilities: ['code', 'chat', 'summarization', 'translation'],
|
|
148
|
+
qualityScore: 7,
|
|
149
|
+
speedScore: 9,
|
|
150
|
+
costScore: 7,
|
|
151
|
+
},
|
|
152
|
+
// Google
|
|
153
|
+
{
|
|
154
|
+
id: 'gemini-2.0-flash',
|
|
155
|
+
provider: 'google',
|
|
156
|
+
displayName: 'Gemini 2.0 Flash',
|
|
157
|
+
inputPricePer1M: 0.10,
|
|
158
|
+
outputPricePer1M: 0.40,
|
|
159
|
+
contextWindow: 1048576,
|
|
160
|
+
maxOutputTokens: 8192,
|
|
161
|
+
capabilities: ['code', 'chat', 'summarization', 'translation', 'analysis', 'math'],
|
|
162
|
+
qualityScore: 7,
|
|
163
|
+
speedScore: 10,
|
|
164
|
+
costScore: 10,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 'gemini-2.0-flash-lite',
|
|
168
|
+
provider: 'google',
|
|
169
|
+
displayName: 'Gemini 2.0 Flash Lite',
|
|
170
|
+
inputPricePer1M: 0.075,
|
|
171
|
+
outputPricePer1M: 0.30,
|
|
172
|
+
contextWindow: 1048576,
|
|
173
|
+
maxOutputTokens: 8192,
|
|
174
|
+
capabilities: ['chat', 'summarization', 'translation'],
|
|
175
|
+
qualityScore: 6,
|
|
176
|
+
speedScore: 10,
|
|
177
|
+
costScore: 10,
|
|
178
|
+
},
|
|
179
|
+
// DeepSeek
|
|
180
|
+
{
|
|
181
|
+
id: 'deepseek-chat',
|
|
182
|
+
provider: 'deepseek',
|
|
183
|
+
displayName: 'DeepSeek V3',
|
|
184
|
+
inputPricePer1M: 0.27,
|
|
185
|
+
outputPricePer1M: 1.10,
|
|
186
|
+
contextWindow: 65536,
|
|
187
|
+
maxOutputTokens: 8192,
|
|
188
|
+
capabilities: ['code', 'chat', 'reasoning', 'analysis', 'math'],
|
|
189
|
+
qualityScore: 8,
|
|
190
|
+
speedScore: 8,
|
|
191
|
+
costScore: 8,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: 'deepseek-reasoner',
|
|
195
|
+
provider: 'deepseek',
|
|
196
|
+
displayName: 'DeepSeek R1',
|
|
197
|
+
inputPricePer1M: 0.55,
|
|
198
|
+
outputPricePer1M: 2.19,
|
|
199
|
+
contextWindow: 65536,
|
|
200
|
+
maxOutputTokens: 8192,
|
|
201
|
+
capabilities: ['reasoning', 'math', 'code', 'analysis'],
|
|
202
|
+
qualityScore: 9,
|
|
203
|
+
speedScore: 5,
|
|
204
|
+
costScore: 7,
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
// ---- Budget Defaults ----
|
|
208
|
+
function getDefaultBudget() {
|
|
209
|
+
return {
|
|
210
|
+
dailyBudget: exports.env.DEFAULT_DAILY_BUDGET,
|
|
211
|
+
monthlyBudget: exports.env.DEFAULT_MONTHLY_BUDGET,
|
|
212
|
+
perRequestCap: exports.env.DEFAULT_PER_REQUEST_CAP,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
// ---- Database Path ----
|
|
216
|
+
function getDbPath() {
|
|
217
|
+
return path_1.default.resolve(process.cwd(), 'data', 'budgetllm.db');
|
|
218
|
+
}
|
|
219
|
+
const LOG_LEVELS = {
|
|
220
|
+
debug: 0,
|
|
221
|
+
info: 1,
|
|
222
|
+
warn: 2,
|
|
223
|
+
error: 3,
|
|
224
|
+
};
|
|
225
|
+
function shouldLog(level) {
|
|
226
|
+
return LOG_LEVELS[level] >= LOG_LEVELS[exports.env.LOG_LEVEL];
|
|
227
|
+
}
|
|
228
|
+
exports.log = {
|
|
229
|
+
debug: (msg, data) => {
|
|
230
|
+
if (shouldLog('debug'))
|
|
231
|
+
console.debug(`[DEBUG] ${msg}`, data ?? '');
|
|
232
|
+
},
|
|
233
|
+
info: (msg, data) => {
|
|
234
|
+
if (shouldLog('info'))
|
|
235
|
+
console.info(`[INFO] ${msg}`, data ?? '');
|
|
236
|
+
},
|
|
237
|
+
warn: (msg, data) => {
|
|
238
|
+
if (shouldLog('warn'))
|
|
239
|
+
console.warn(`[WARN] ${msg}`, data ?? '');
|
|
240
|
+
},
|
|
241
|
+
error: (msg, data) => {
|
|
242
|
+
if (shouldLog('error'))
|
|
243
|
+
console.error(`[ERROR] ${msg}`, data ?? '');
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|