@relayplane/proxy 1.1.0 → 1.1.2

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.
Files changed (38) hide show
  1. package/README.md +120 -221
  2. package/dist/__tests__/model-suggestions.test.d.ts +2 -0
  3. package/dist/__tests__/model-suggestions.test.d.ts.map +1 -0
  4. package/dist/__tests__/model-suggestions.test.js +67 -0
  5. package/dist/__tests__/model-suggestions.test.js.map +1 -0
  6. package/dist/__tests__/routing-aliases.test.d.ts +2 -0
  7. package/dist/__tests__/routing-aliases.test.d.ts.map +1 -0
  8. package/dist/__tests__/routing-aliases.test.js +81 -0
  9. package/dist/__tests__/routing-aliases.test.js.map +1 -0
  10. package/dist/cli.d.ts +36 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +304 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/config.d.ts +80 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +208 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/index.d.ts +27 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +60 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/standalone-proxy.d.ts +101 -0
  23. package/dist/standalone-proxy.d.ts.map +1 -0
  24. package/dist/standalone-proxy.js +2524 -0
  25. package/dist/standalone-proxy.js.map +1 -0
  26. package/dist/swarm-client.d.ts +87 -0
  27. package/dist/swarm-client.d.ts.map +1 -0
  28. package/dist/swarm-client.js +205 -0
  29. package/dist/swarm-client.js.map +1 -0
  30. package/dist/telemetry.d.ts +127 -0
  31. package/dist/telemetry.d.ts.map +1 -0
  32. package/dist/telemetry.js +426 -0
  33. package/dist/telemetry.js.map +1 -0
  34. package/dist/utils/model-suggestions.d.ts +28 -0
  35. package/dist/utils/model-suggestions.d.ts.map +1 -0
  36. package/dist/utils/model-suggestions.js +50 -0
  37. package/dist/utils/model-suggestions.js.map +1 -0
  38. package/package.json +35 -29
package/README.md CHANGED
@@ -1,286 +1,185 @@
1
1
  # @relayplane/proxy
2
2
 
3
- Local LLM proxy server for RelayPlane - route requests through multiple AI providers.
4
-
5
- ## What's New in 1.1
6
-
7
- - 🩺 **Health Endpoint** — `GET /health` with uptime, stats, and provider status
8
- - ⚠️ **Usage Warnings** — Console and header warnings at 80%, 90%, 100% of limits
9
- - 📊 **Response Headers** — `X-RelayPlane-Daily-Usage`, `X-RelayPlane-Monthly-Usage`, `X-RelayPlane-Usage-Warning`
10
- - 💰 **Spending Limits** — Configure `limits.daily` and `limits.monthly` with 429 when exceeded
11
- - 🏷️ **Model Aliases** — `rp:fast`, `rp:cheap`, `rp:best`, `rp:balanced` shortcuts
12
-
13
- ## Features
14
-
15
- - **OpenAI-compatible API** - Drop-in replacement for OpenAI SDK
16
- - **Multi-provider routing** - Automatically routes to OpenAI, Anthropic, Groq, Together, OpenRouter
17
- - **Model aliases** - `rp:fast`, `rp:cheap`, `rp:best` shortcuts
18
- - **Dry-run mode** - Test routing without making API calls
19
- - **Usage tracking** - Track tokens, cost, and latency
20
- - **Spending limits** - Daily/monthly cost limits with warnings
21
- - **Health endpoint** - `/health` for monitoring and uptime checks
3
+ Intelligent AI model routing proxy for cost optimization and observability.
22
4
 
23
5
  ## Installation
24
6
 
25
7
  ```bash
26
- npm install @relayplane/proxy
27
- ```
28
-
29
- Or use via the CLI:
30
-
31
- ```bash
32
- npm install -g @relayplane/cli
33
- relayplane proxy start
8
+ npm install -g @relayplane/proxy
34
9
  ```
35
10
 
36
11
  ## Quick Start
37
12
 
38
13
  ```bash
39
- # Set API keys
40
- export OPENAI_API_KEY=sk-...
41
- export ANTHROPIC_API_KEY=sk-ant-...
14
+ # Set your API keys
15
+ export ANTHROPIC_API_KEY=your-key
16
+ export OPENAI_API_KEY=your-key
42
17
 
43
18
  # Start the proxy
44
- npx @relayplane/proxy
45
-
46
- # Make requests
47
- curl http://localhost:8787/v1/chat/completions \
48
- -H "Content-Type: application/json" \
49
- -d '{
50
- "model": "gpt-4o",
51
- "messages": [{"role": "user", "content": "Hello!"}]
52
- }'
19
+ relayplane-proxy
20
+
21
+ # Configure your tools to use the proxy
22
+ export ANTHROPIC_BASE_URL=http://localhost:3001
23
+ export OPENAI_BASE_URL=http://localhost:3001
24
+
25
+ # Run your AI tools (Claude Code, Cursor, Aider, etc.)
53
26
  ```
54
27
 
55
- ## Endpoints
28
+ ## Features
56
29
 
57
- ### `GET /health`
30
+ - **Intelligent Routing**: Routes requests to the optimal model based on task type
31
+ - **Cost Tracking**: Tracks and reports API costs across all providers
32
+ - **Provider Agnostic**: Works with Anthropic, OpenAI, Gemini, xAI, and more
33
+ - **Local Learning**: Learns from your usage patterns to improve routing
34
+ - **Privacy First**: Never sees your prompts or responses
58
35
 
59
- Health check endpoint for monitoring.
36
+ ## CLI Options
60
37
 
61
38
  ```bash
62
- curl http://localhost:8787/health
39
+ relayplane-proxy [command] [options]
40
+
41
+ Commands:
42
+ (default) Start the proxy server
43
+ telemetry [on|off|status] Manage telemetry settings
44
+ stats Show usage statistics
45
+ config Show configuration
46
+
47
+ Options:
48
+ --port <number> Port to listen on (default: 3001)
49
+ --host <string> Host to bind to (default: 127.0.0.1)
50
+ --offline Disable all network calls except LLM endpoints
51
+ --audit Show telemetry payloads before sending
52
+ -v, --verbose Enable verbose logging
53
+ -h, --help Show this help message
54
+ --version Show version
63
55
  ```
64
56
 
65
- Response:
57
+ ## Telemetry
58
+
59
+ RelayPlane collects anonymous telemetry to improve model routing. This data helps us understand usage patterns and optimize routing decisions.
60
+
61
+ ### What We Collect (Exact Schema)
62
+
66
63
  ```json
67
64
  {
68
- "status": "ok",
69
- "uptime": 3600,
70
- "version": "1.1.0",
71
- "providers": {
72
- "openai": "configured",
73
- "anthropic": "configured",
74
- "groq": "not_configured",
75
- "together": "not_configured",
76
- "openrouter": "not_configured"
77
- },
78
- "requestsHandled": 150,
79
- "requestsSuccessful": 148,
80
- "requestsFailed": 2,
81
- "dailyCost": 1.25,
82
- "dailyLimit": 10.00,
83
- "monthlyCost": 25.50,
84
- "monthlyLimit": 100.00,
85
- "usage": {
86
- "inputTokens": 50000,
87
- "outputTokens": 25000,
88
- "totalCost": 1.25
89
- }
65
+ "device_id": "anon_8f3a...",
66
+ "task_type": "code_review",
67
+ "model": "claude-3-5-haiku",
68
+ "tokens_in": 1847,
69
+ "tokens_out": 423,
70
+ "latency_ms": 2341,
71
+ "success": true,
72
+ "cost_usd": 0.02
90
73
  }
91
74
  ```
92
75
 
93
- ### `GET /v1/models`
76
+ ### Field Descriptions
94
77
 
95
- List available models including aliases.
78
+ | Field | Type | Description |
79
+ |-------|------|-------------|
80
+ | `device_id` | string | Anonymous random ID (not fingerprintable) |
81
+ | `task_type` | string | Inferred from token patterns, NOT prompt content |
82
+ | `model` | string | The model that handled the request |
83
+ | `tokens_in` | number | Input token count |
84
+ | `tokens_out` | number | Output token count |
85
+ | `latency_ms` | number | Request latency in milliseconds |
86
+ | `success` | boolean | Whether the request succeeded |
87
+ | `cost_usd` | number | Estimated cost in USD |
96
88
 
97
- ```bash
98
- curl http://localhost:8787/v1/models
99
- ```
89
+ ### Task Types
100
90
 
101
- ### `POST /v1/chat/completions`
91
+ Task types are inferred from request characteristics (token counts, ratios, etc.) - never from prompt content:
102
92
 
103
- OpenAI-compatible chat completions.
93
+ - `quick_task` - Short input/output (< 500 tokens each)
94
+ - `code_review` - Medium-long input, medium output
95
+ - `generation` - High output/input ratio
96
+ - `classification` - Low output/input ratio, short output
97
+ - `long_context` - Input > 10,000 tokens
98
+ - `content_generation` - Output > 1,000 tokens
99
+ - `tool_use` - Request includes tool calls
100
+ - `general` - Default classification
104
101
 
105
- ```bash
106
- curl http://localhost:8787/v1/chat/completions \
107
- -H "Content-Type: application/json" \
108
- -d '{
109
- "model": "rp:best",
110
- "messages": [{"role": "user", "content": "Hello!"}]
111
- }'
112
- ```
102
+ ### What We NEVER Collect
113
103
 
114
- ## Model Aliases
104
+ - Your prompts
105
+ - ❌ Model responses
106
+ - ❌ File paths or contents
107
+ - ❌ Anything that could identify you or your project
115
108
 
116
- | Alias | Resolves To | Provider | Use Case |
117
- |-------|-------------|----------|----------|
118
- | `rp:fast` | llama-3.1-8b-instant | Groq | Lowest latency |
119
- | `rp:cheap` | llama-3.1-8b-instant | Groq | Lowest cost |
120
- | `rp:best` | claude-3-5-sonnet-20241022 | Anthropic | Highest quality |
121
- | `rp:balanced` | gpt-4o-mini | OpenAI | Good balance |
109
+ ### Verification
122
110
 
123
- ## Dry-Run Mode
124
-
125
- Test routing logic without making API calls:
111
+ You can verify exactly what data is collected:
126
112
 
127
113
  ```bash
128
- curl http://localhost:8787/v1/chat/completions \
129
- -H "Content-Type: application/json" \
130
- -H "X-Dry-Run: true" \
131
- -d '{
132
- "model": "gpt-4o",
133
- "messages": [{"role": "user", "content": "Hello!"}]
134
- }'
135
- ```
136
-
137
- Response:
138
- ```json
139
- {
140
- "dry_run": true,
141
- "routing": {
142
- "model": "gpt-4o",
143
- "provider": "openai",
144
- "endpoint": "https://api.openai.com/v1/chat/completions"
145
- },
146
- "estimate": {
147
- "inputTokens": 10,
148
- "expectedOutputTokens": 500,
149
- "estimatedCost": 0.0125,
150
- "currency": "USD"
151
- },
152
- "limits": {
153
- "daily": 10.00,
154
- "dailyUsed": 1.25,
155
- "dailyRemaining": 8.75,
156
- "monthly": 100.00,
157
- "monthlyUsed": 25.50,
158
- "monthlyRemaining": 74.50
159
- }
160
- }
161
- ```
114
+ # See telemetry payloads before they're sent
115
+ relayplane-proxy --audit
162
116
 
163
- ## Response Headers
117
+ # Disable all telemetry transmission
118
+ relayplane-proxy --offline
164
119
 
165
- The proxy adds usage information to response headers:
120
+ # View the source code
121
+ # https://github.com/RelayPlane/proxy
122
+ ```
166
123
 
167
- | Header | Description |
168
- |--------|-------------|
169
- | `X-RelayPlane-Cost` | Cost of this request |
170
- | `X-RelayPlane-Latency` | Request latency in ms |
171
- | `X-RelayPlane-Daily-Usage` | Daily usage (e.g., "1.25/10.00") |
172
- | `X-RelayPlane-Monthly-Usage` | Monthly usage (e.g., "25.50/100.00") |
173
- | `X-RelayPlane-Usage-Warning` | Warning when approaching limits (80%, 90%, 100%) |
124
+ ### Opt-Out
174
125
 
175
- Example warning header:
176
- ```
177
- X-RelayPlane-Usage-Warning: ⚠️ You've used $8.50 of your $10 daily limit
178
- ```
126
+ To disable telemetry completely:
179
127
 
180
- Console warnings are also logged when approaching limits:
181
- ```
182
- ⚠️ Daily spending at 80%: $8.00 / $10
183
- ⚠️ Daily spending at 90%: $9.00 / $10
184
- ⚠️ DAILY LIMIT REACHED: $10.00 / $10 (100%)
128
+ ```bash
129
+ relayplane-proxy telemetry off
185
130
  ```
186
131
 
187
- ## Spending Limits
188
-
189
- Configure limits in `~/.relayplane/config.json`:
132
+ To re-enable:
190
133
 
191
- ```json
192
- {
193
- "limits": {
194
- "daily": 10.00,
195
- "monthly": 100.00
196
- }
197
- }
134
+ ```bash
135
+ relayplane-proxy telemetry on
198
136
  ```
199
137
 
200
- When limits are reached, the proxy returns HTTP `429 Too Many Requests`:
138
+ Check current status:
201
139
 
202
- ```json
203
- {
204
- "error": {
205
- "message": "Daily spending limit reached ($10.00 / $10.00)",
206
- "code": "spending_limit_exceeded",
207
- "type": "rate_limit_error"
208
- }
209
- }
140
+ ```bash
141
+ relayplane-proxy telemetry status
210
142
  ```
211
143
 
212
- Headers included with 429 response:
213
- - `Retry-After: 86400` (seconds until daily reset)
214
- - `X-RelayPlane-Daily-Usage: 10.00/10.00`
144
+ ## Configuration
215
145
 
216
- ## Usage Tracking
146
+ Configuration is stored in `~/.relayplane/config.json`.
217
147
 
218
- Usage is logged to `~/.relayplane/usage.jsonl`:
148
+ ### Set API Key (Pro Features)
219
149
 
220
- ```jsonl
221
- {"timestamp":"2024-01-15T12:00:00Z","model":"gpt-4o","provider":"openai","inputTokens":100,"outputTokens":50,"cost":0.00125,"latencyMs":1500,"success":true}
150
+ ```bash
151
+ relayplane-proxy config set-key your-api-key
222
152
  ```
223
153
 
224
- Daily totals are tracked in `~/.relayplane/daily-usage.json`:
154
+ ### View Configuration
225
155
 
226
- ```json
227
- {
228
- "date": "2024-01-15",
229
- "cost": 1.25,
230
- "requests": 50
231
- }
156
+ ```bash
157
+ relayplane-proxy config
232
158
  ```
233
159
 
234
- Monthly totals are tracked in `~/.relayplane/monthly-usage.json`:
160
+ ## Usage Statistics
235
161
 
236
- ```json
237
- {
238
- "month": "2024-01",
239
- "cost": 25.50,
240
- "requests": 1200
241
- }
162
+ View your usage statistics:
163
+
164
+ ```bash
165
+ relayplane-proxy stats
242
166
  ```
243
167
 
168
+ This shows:
169
+ - Total requests and cost
170
+ - Success rate
171
+ - Breakdown by model
172
+ - Breakdown by task type
173
+
244
174
  ## Environment Variables
245
175
 
246
- | Variable | Default | Description |
247
- |----------|---------|-------------|
248
- | `RELAYPLANE_PROXY_PORT` | 8787 | Port to listen on |
249
- | `RELAYPLANE_PROXY_HOST` | 127.0.0.1 | Host to bind to |
250
- | `RELAYPLANE_CONFIG_DIR` | ~/.relayplane | Config directory |
251
- | `OPENAI_API_KEY` | - | OpenAI API key |
252
- | `ANTHROPIC_API_KEY` | - | Anthropic API key |
253
- | `GROQ_API_KEY` | - | Groq API key |
254
- | `TOGETHER_API_KEY` | - | Together AI API key |
255
- | `OPENROUTER_API_KEY` | - | OpenRouter API key |
256
-
257
- ## Provider Detection
258
-
259
- Models are automatically routed to the correct provider:
260
-
261
- | Pattern | Provider |
262
- |---------|----------|
263
- | `gpt-*`, `o1-*` | OpenAI |
264
- | `claude-*` | Anthropic |
265
- | `llama-*`, `mixtral-*` | Groq |
266
- | `meta-llama/*`, `mistralai/*` | Together |
267
- | Contains `/` | OpenRouter |
268
-
269
- ## Using with OpenAI SDK
270
-
271
- ```python
272
- from openai import OpenAI
273
-
274
- client = OpenAI(
275
- base_url="http://localhost:8787/v1",
276
- api_key="not-needed" # API keys are configured on the proxy
277
- )
278
-
279
- response = client.chat.completions.create(
280
- model="rp:best", # Uses Claude 3.5 Sonnet
281
- messages=[{"role": "user", "content": "Hello!"}]
282
- )
283
- ```
176
+ | Variable | Description |
177
+ |----------|-------------|
178
+ | `ANTHROPIC_API_KEY` | Anthropic API key |
179
+ | `OPENAI_API_KEY` | OpenAI API key |
180
+ | `GEMINI_API_KEY` | Google Gemini API key |
181
+ | `XAI_API_KEY` | xAI/Grok API key |
182
+ | `MOONSHOT_API_KEY` | Moonshot API key |
284
183
 
285
184
  ## License
286
185
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=model-suggestions.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-suggestions.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/model-suggestions.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const model_suggestions_js_1 = require("../utils/model-suggestions.js");
5
+ (0, vitest_1.describe)('suggestModels', () => {
6
+ const availableModels = [
7
+ 'claude-sonnet-4',
8
+ 'claude-opus-4-5',
9
+ 'claude-3-5-sonnet',
10
+ 'claude-3-5-haiku',
11
+ 'gpt-4o',
12
+ 'gpt-4o-mini',
13
+ 'rp:best',
14
+ 'rp:fast',
15
+ 'rp:balanced',
16
+ ];
17
+ (0, vitest_1.it)('should suggest similar models for typos', () => {
18
+ const suggestions = (0, model_suggestions_js_1.suggestModels)('claud-sonnet', availableModels);
19
+ (0, vitest_1.expect)(suggestions).toContain('claude-sonnet-4');
20
+ });
21
+ (0, vitest_1.it)('should suggest claude-sonnet-4 for "claude-sonet-4" typo', () => {
22
+ const suggestions = (0, model_suggestions_js_1.suggestModels)('claude-sonet-4', availableModels);
23
+ (0, vitest_1.expect)(suggestions[0]).toBe('claude-sonnet-4');
24
+ });
25
+ (0, vitest_1.it)('should suggest gpt-4o for "gpt4o" typo', () => {
26
+ const suggestions = (0, model_suggestions_js_1.suggestModels)('gpt4o', availableModels);
27
+ (0, vitest_1.expect)(suggestions).toContain('gpt-4o');
28
+ });
29
+ (0, vitest_1.it)('should return empty array for completely different strings', () => {
30
+ const suggestions = (0, model_suggestions_js_1.suggestModels)('totally-random-model-xyz', availableModels);
31
+ (0, vitest_1.expect)(suggestions.length).toBeLessThanOrEqual(3);
32
+ });
33
+ (0, vitest_1.it)('should limit results to max parameter', () => {
34
+ const suggestions = (0, model_suggestions_js_1.suggestModels)('claude', availableModels, 2);
35
+ (0, vitest_1.expect)(suggestions.length).toBeLessThanOrEqual(2);
36
+ });
37
+ (0, vitest_1.it)('should be case-insensitive', () => {
38
+ const suggestions = (0, model_suggestions_js_1.suggestModels)('CLAUDE-SONNET-4', availableModels);
39
+ (0, vitest_1.expect)(suggestions).toContain('claude-sonnet-4');
40
+ });
41
+ (0, vitest_1.it)('should suggest rp:best for "rp:bst" typo', () => {
42
+ const suggestions = (0, model_suggestions_js_1.suggestModels)('rp:bst', availableModels);
43
+ (0, vitest_1.expect)(suggestions).toContain('rp:best');
44
+ });
45
+ });
46
+ (0, vitest_1.describe)('buildModelNotFoundError', () => {
47
+ const availableModels = [
48
+ 'claude-sonnet-4',
49
+ 'claude-opus-4-5',
50
+ 'gpt-4o',
51
+ ];
52
+ (0, vitest_1.it)('should build error with suggestions for close match', () => {
53
+ const error = (0, model_suggestions_js_1.buildModelNotFoundError)('claude-sonet-4', availableModels);
54
+ (0, vitest_1.expect)(error.error).toContain('claude-sonet-4');
55
+ (0, vitest_1.expect)(error.error).toContain('does not exist');
56
+ (0, vitest_1.expect)(error.suggestions).toBeDefined();
57
+ (0, vitest_1.expect)(error.suggestions).toContain('claude-sonnet-4');
58
+ (0, vitest_1.expect)(error.hint).toBe("Did you mean 'claude-sonnet-4'?");
59
+ });
60
+ (0, vitest_1.it)('should build error without suggestions for no close match', () => {
61
+ const error = (0, model_suggestions_js_1.buildModelNotFoundError)('totally-random-xyz-12345', availableModels);
62
+ (0, vitest_1.expect)(error.error).toContain('totally-random-xyz-12345');
63
+ (0, vitest_1.expect)(error.suggestions).toBeUndefined();
64
+ (0, vitest_1.expect)(error.hint).toBeUndefined();
65
+ });
66
+ });
67
+ //# sourceMappingURL=model-suggestions.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-suggestions.test.js","sourceRoot":"","sources":["../../src/__tests__/model-suggestions.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=routing-aliases.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing-aliases.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/routing-aliases.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const standalone_proxy_js_1 = require("../standalone-proxy.js");
5
+ (0, vitest_1.describe)('RELAYPLANE_ALIASES', () => {
6
+ (0, vitest_1.it)('should map relayplane:auto to rp:balanced', () => {
7
+ (0, vitest_1.expect)(standalone_proxy_js_1.RELAYPLANE_ALIASES['relayplane:auto']).toBe('rp:balanced');
8
+ });
9
+ (0, vitest_1.it)('should map rp:auto to rp:balanced', () => {
10
+ (0, vitest_1.expect)(standalone_proxy_js_1.RELAYPLANE_ALIASES['rp:auto']).toBe('rp:balanced');
11
+ });
12
+ });
13
+ (0, vitest_1.describe)('SMART_ALIASES', () => {
14
+ (0, vitest_1.it)('should have rp:best pointing to a valid model', () => {
15
+ (0, vitest_1.expect)(standalone_proxy_js_1.SMART_ALIASES['rp:best']).toBeDefined();
16
+ (0, vitest_1.expect)(standalone_proxy_js_1.SMART_ALIASES['rp:best'].provider).toBe('anthropic');
17
+ (0, vitest_1.expect)(standalone_proxy_js_1.SMART_ALIASES['rp:best'].model).toContain('claude');
18
+ });
19
+ (0, vitest_1.it)('should have rp:fast pointing to a fast model', () => {
20
+ (0, vitest_1.expect)(standalone_proxy_js_1.SMART_ALIASES['rp:fast']).toBeDefined();
21
+ (0, vitest_1.expect)(standalone_proxy_js_1.SMART_ALIASES['rp:fast'].model).toContain('haiku');
22
+ });
23
+ (0, vitest_1.it)('should have rp:cheap pointing to a cheap model', () => {
24
+ (0, vitest_1.expect)(standalone_proxy_js_1.SMART_ALIASES['rp:cheap']).toBeDefined();
25
+ (0, vitest_1.expect)(standalone_proxy_js_1.SMART_ALIASES['rp:cheap'].model).toContain('mini');
26
+ });
27
+ (0, vitest_1.it)('should have rp:balanced pointing to a balanced model', () => {
28
+ (0, vitest_1.expect)(standalone_proxy_js_1.SMART_ALIASES['rp:balanced']).toBeDefined();
29
+ });
30
+ (0, vitest_1.it)('should point to existing models', () => {
31
+ // Verify model names are valid API model IDs
32
+ (0, vitest_1.expect)(standalone_proxy_js_1.SMART_ALIASES['rp:best'].model).toMatch(/claude-.*-\d{8}$/);
33
+ (0, vitest_1.expect)(standalone_proxy_js_1.SMART_ALIASES['rp:fast'].model).toMatch(/claude-.*-\d{8}$/);
34
+ (0, vitest_1.expect)(standalone_proxy_js_1.SMART_ALIASES['rp:balanced'].model).toMatch(/claude-.*-\d{8}$/);
35
+ });
36
+ });
37
+ (0, vitest_1.describe)('resolveModelAlias', () => {
38
+ (0, vitest_1.it)('should resolve relayplane:auto to rp:balanced', () => {
39
+ (0, vitest_1.expect)((0, standalone_proxy_js_1.resolveModelAlias)('relayplane:auto')).toBe('rp:balanced');
40
+ });
41
+ (0, vitest_1.it)('should resolve rp:auto to rp:balanced', () => {
42
+ (0, vitest_1.expect)((0, standalone_proxy_js_1.resolveModelAlias)('rp:auto')).toBe('rp:balanced');
43
+ });
44
+ (0, vitest_1.it)('should return unchanged for non-alias models', () => {
45
+ (0, vitest_1.expect)((0, standalone_proxy_js_1.resolveModelAlias)('claude-sonnet-4')).toBe('claude-sonnet-4');
46
+ (0, vitest_1.expect)((0, standalone_proxy_js_1.resolveModelAlias)('gpt-4o')).toBe('gpt-4o');
47
+ (0, vitest_1.expect)((0, standalone_proxy_js_1.resolveModelAlias)('rp:best')).toBe('rp:best');
48
+ });
49
+ (0, vitest_1.it)('should return unchanged for unknown models', () => {
50
+ (0, vitest_1.expect)((0, standalone_proxy_js_1.resolveModelAlias)('unknown-model')).toBe('unknown-model');
51
+ });
52
+ });
53
+ (0, vitest_1.describe)('getAvailableModelNames', () => {
54
+ (0, vitest_1.it)('should include MODEL_MAPPING keys', () => {
55
+ const available = (0, standalone_proxy_js_1.getAvailableModelNames)();
56
+ (0, vitest_1.expect)(available).toContain('claude-sonnet-4');
57
+ (0, vitest_1.expect)(available).toContain('gpt-4o');
58
+ });
59
+ (0, vitest_1.it)('should include SMART_ALIASES keys', () => {
60
+ const available = (0, standalone_proxy_js_1.getAvailableModelNames)();
61
+ (0, vitest_1.expect)(available).toContain('rp:best');
62
+ (0, vitest_1.expect)(available).toContain('rp:fast');
63
+ (0, vitest_1.expect)(available).toContain('rp:balanced');
64
+ });
65
+ (0, vitest_1.it)('should include relayplane routing models', () => {
66
+ const available = (0, standalone_proxy_js_1.getAvailableModelNames)();
67
+ (0, vitest_1.expect)(available).toContain('relayplane:auto');
68
+ (0, vitest_1.expect)(available).toContain('relayplane:cost');
69
+ (0, vitest_1.expect)(available).toContain('relayplane:fast');
70
+ (0, vitest_1.expect)(available).toContain('relayplane:quality');
71
+ });
72
+ });
73
+ (0, vitest_1.describe)('MODEL_MAPPING', () => {
74
+ (0, vitest_1.it)('should have updated sonnet pointing to claude-sonnet-4', () => {
75
+ (0, vitest_1.expect)(standalone_proxy_js_1.MODEL_MAPPING['sonnet'].model).toContain('claude-sonnet-4');
76
+ });
77
+ (0, vitest_1.it)('should have updated opus pointing to claude-opus-4-5', () => {
78
+ (0, vitest_1.expect)(standalone_proxy_js_1.MODEL_MAPPING['opus'].model).toContain('claude-opus-4-5');
79
+ });
80
+ });
81
+ //# sourceMappingURL=routing-aliases.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing-aliases.test.js","sourceRoot":"","sources":["../../src/__tests__/routing-aliases.test.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * RelayPlane Proxy CLI
4
+ *
5
+ * Intelligent AI model routing proxy server.
6
+ *
7
+ * Usage:
8
+ * npx @relayplane/proxy [command] [options]
9
+ * relayplane-proxy [command] [options]
10
+ *
11
+ * Commands:
12
+ * (default) Start the proxy server
13
+ * telemetry [on|off|status] Manage telemetry settings
14
+ * stats Show usage statistics
15
+ * config Show configuration
16
+ *
17
+ * Options:
18
+ * --port <number> Port to listen on (default: 3001)
19
+ * --host <string> Host to bind to (default: 127.0.0.1)
20
+ * --offline Disable all network calls except LLM endpoints
21
+ * --audit Show telemetry payloads before sending
22
+ * -v, --verbose Enable verbose logging
23
+ * -h, --help Show this help message
24
+ * --version Show version
25
+ *
26
+ * Environment Variables:
27
+ * ANTHROPIC_API_KEY Anthropic API key
28
+ * OPENAI_API_KEY OpenAI API key
29
+ * GEMINI_API_KEY Google Gemini API key
30
+ * XAI_API_KEY xAI/Grok API key
31
+ * MOONSHOT_API_KEY Moonshot API key
32
+ *
33
+ * @packageDocumentation
34
+ */
35
+ export {};
36
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG"}