@gabrielsmartin/orbit-sdk 0.4.4 → 0.5.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/README.md +5 -3
- package/package.json +5 -3
- package/scripts/postinstall.cjs +36 -0
- package/src/fingerprint.js +8 -4
- package/src/index.d.ts +7 -3
- package/src/index.js +44 -24
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ import orbit from '@gabrielsmartin/orbit-sdk'
|
|
|
37
37
|
// Route a query — returns decision instantly, no network call
|
|
38
38
|
const { model, reason, savings } = orbit.route("write a haiku about recursion")
|
|
39
39
|
// model.name → "Claude Sonnet"
|
|
40
|
-
// model.id → "claude-sonnet-
|
|
40
|
+
// model.id → "claude-sonnet-4-6"
|
|
41
41
|
// reason → "High creativity — Claude Sonnet for open-ended generation."
|
|
42
42
|
// savings.reductionPct → 50
|
|
43
43
|
|
|
@@ -154,11 +154,13 @@ curl -X POST https://api.gtll.app/orbitRoute \
|
|
|
154
154
|
| Tier | Price | Limit |
|
|
155
155
|
|------|-------|-------|
|
|
156
156
|
| Free | $0/mo | 100 queries/day |
|
|
157
|
-
| Pro |
|
|
158
|
-
| Team |
|
|
157
|
+
| Pro | $19/mo | Unlimited queries |
|
|
158
|
+
| Team | $99/mo | Unlimited · 5 seats |
|
|
159
159
|
|
|
160
160
|
→ [Get Pro](https://buy.stripe.com/7sY14mdHYbTE3DT4PIbwk00) · [Get Team](https://buy.stripe.com/9B67sKfQ6bTE0rHgyqbwk01)
|
|
161
161
|
|
|
162
|
+
> **Founding access:** Use code `777` (Pro $9/mo), `555` (Team $49/mo), or `333` (Free+) at [orbitai.gtll.app](https://orbitai.gtll.app) — locked forever at the founding rate.
|
|
163
|
+
|
|
162
164
|
---
|
|
163
165
|
|
|
164
166
|
## Research backing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gabrielsmartin/orbit-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Rule-based LLM router. Classifies queries across 8 axes and picks the optimal model. Fast, deterministic, zero dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -13,10 +13,12 @@
|
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"src",
|
|
16
|
+
"scripts",
|
|
16
17
|
"README.md"
|
|
17
18
|
],
|
|
18
19
|
"scripts": {
|
|
19
|
-
"test": "node test.js"
|
|
20
|
+
"test": "node test.js",
|
|
21
|
+
"postinstall": "node scripts/postinstall.cjs"
|
|
20
22
|
},
|
|
21
23
|
"keywords": [
|
|
22
24
|
"ai",
|
|
@@ -41,7 +43,7 @@
|
|
|
41
43
|
"type": "git",
|
|
42
44
|
"url": "https://github.com/gtllco/orbit.git"
|
|
43
45
|
},
|
|
44
|
-
"homepage": "https://
|
|
46
|
+
"homepage": "https://github.com/gtllco/orbit",
|
|
45
47
|
"bugs": {
|
|
46
48
|
"url": "https://github.com/gtllco/orbit/issues"
|
|
47
49
|
},
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// Fires on `npm install @gabrielsmartin/orbit-sdk`
|
|
5
|
+
// Captures install signal → ORBIT gateway → Supabase
|
|
6
|
+
// Non-blocking, silently fails, no PII
|
|
7
|
+
|
|
8
|
+
const https = require('https');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
|
|
11
|
+
const payload = JSON.stringify({
|
|
12
|
+
action: 'npm_install',
|
|
13
|
+
package: '@gabrielsmartin/orbit-sdk',
|
|
14
|
+
version: require('../package.json').version,
|
|
15
|
+
node: process.version,
|
|
16
|
+
platform: process.platform,
|
|
17
|
+
arch: process.arch,
|
|
18
|
+
ts: new Date().toISOString(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const req = https.request({
|
|
22
|
+
hostname: 'api.gtll.app',
|
|
23
|
+
path: '/npmPing',
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
|
|
26
|
+
timeout: 3000,
|
|
27
|
+
}, () => {});
|
|
28
|
+
|
|
29
|
+
req.on('error', () => {});
|
|
30
|
+
req.on('timeout', () => req.destroy());
|
|
31
|
+
req.write(payload);
|
|
32
|
+
req.end();
|
|
33
|
+
|
|
34
|
+
console.log('\n\x1b[35m ⊙ ORBIT SDK installed\x1b[0m');
|
|
35
|
+
console.log('\x1b[2m Get your free API key + routing analytics:\x1b[0m');
|
|
36
|
+
console.log('\x1b[36m https://orbitai.gtll.app\x1b[0m\n');
|
package/src/fingerprint.js
CHANGED
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
const COMPLEXITY_SIGNALS = [
|
|
10
10
|
'architect','distributed','implement','design','optimize','analyze',
|
|
11
11
|
'complex','system','algorithm','concurrent','scale','infrastructure',
|
|
12
|
-
'microservice','kubernetes','terraform','recursive','refactor'
|
|
12
|
+
'microservice','kubernetes','terraform','recursive','refactor',
|
|
13
|
+
'rate limit','concurren','async','thread','cache','shard','partition',
|
|
14
|
+
'replicate','encrypt','auth','oauth','webhook','pipeline','stream',
|
|
15
|
+
'batch','migration','rollback','schema','index','transaction','latency',
|
|
16
|
+
'throughput','bottleneck','profil','benchmark','test','spec'
|
|
13
17
|
];
|
|
14
18
|
|
|
15
19
|
const CREATIVITY_SIGNALS = [
|
|
@@ -46,11 +50,11 @@ export function fingerprint(text) {
|
|
|
46
50
|
|
|
47
51
|
// Complexity (0-10): reasoning depth required
|
|
48
52
|
const complexitySignals = COMPLEXITY_SIGNALS.filter(s => t.includes(s)).length;
|
|
49
|
-
const questionDepth = (t.match(/\b(why|how|explain|compare|tradeoff|pros|cons|difference|analyze)\b/g) || []).length;
|
|
53
|
+
const questionDepth = (t.match(/\b(why|how|explain|compare|tradeoff|tradeoffs|versus|vs|pros|cons|difference|analyze|evaluate|review|audit)\b/g) || []).length;
|
|
50
54
|
const complexity = Math.min(10, Math.round(
|
|
51
|
-
complexitySignals * 2 +
|
|
55
|
+
complexitySignals * 2.5 +
|
|
52
56
|
questionDepth * 1.5 +
|
|
53
|
-
(wordCount >
|
|
57
|
+
(wordCount > 20 ? 3 : wordCount > 10 ? 1.5 : 0)
|
|
54
58
|
));
|
|
55
59
|
|
|
56
60
|
// Creativity (0-10): open-ended vs deterministic
|
package/src/index.d.ts
CHANGED
|
@@ -32,12 +32,15 @@ export interface SavingsInfo {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export interface RoutingDecision {
|
|
35
|
-
model: ModelInfo;
|
|
35
|
+
model: ModelInfo | null;
|
|
36
36
|
reason: string;
|
|
37
37
|
rule: string;
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
signal: '777' | '555' | '333' | null;
|
|
39
|
+
signal_reason: string | null;
|
|
40
|
+
scores: QueryScores | null;
|
|
41
|
+
savings: SavingsInfo | null;
|
|
40
42
|
timestamp: string;
|
|
43
|
+
blocked: boolean;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
export interface OrbitConfig {
|
|
@@ -55,6 +58,7 @@ export interface RouteOptions {
|
|
|
55
58
|
cost_tolerance?: 'low' | 'medium' | 'high';
|
|
56
59
|
estimated_tokens?: number;
|
|
57
60
|
blocked_models?: string[];
|
|
61
|
+
signal?: '777' | '555' | '333' | null;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
export interface SessionStats {
|
package/src/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @gabrielsmartin/orbit-sdk
|
|
3
3
|
* Rule-based LLM router. Fast, deterministic, zero dependencies.
|
|
4
4
|
* Picks the right model — you make the API call.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* https://github.com/gtllco/orbit
|
|
7
7
|
* 777 · 555 · 333
|
|
8
8
|
*/
|
|
@@ -13,13 +13,14 @@ import { route, calculateSavings, MODEL_MATRIX } from './router.js';
|
|
|
13
13
|
export { fingerprint, applySignalBias, route, calculateSavings, MODEL_MATRIX };
|
|
14
14
|
|
|
15
15
|
const GATEWAY_URL = 'https://api.gtll.app/orbitSignup';
|
|
16
|
+
const ANON_QUERY_LIMIT = 10;
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* OrbitClient — the main class
|
|
19
|
-
*
|
|
20
|
+
*
|
|
20
21
|
* @example
|
|
21
22
|
* import { OrbitClient } from '@gabrielsmartin/orbit-sdk'
|
|
22
|
-
*
|
|
23
|
+
*
|
|
23
24
|
* const orbit = new OrbitClient({ apiKey: 'your-key' })
|
|
24
25
|
* const { model, reason, scores } = orbit.route("explain quantum entanglement")
|
|
25
26
|
* // → { model: { name: 'Claude Sonnet', id: 'claude-sonnet-3-5', ... }, reason: '...', ... }
|
|
@@ -46,12 +47,30 @@ export class OrbitClient {
|
|
|
46
47
|
/**
|
|
47
48
|
* Route a query to the optimal model (local, <1ms).
|
|
48
49
|
* ORBIT picks the model — your code makes the API call.
|
|
49
|
-
*
|
|
50
|
+
*
|
|
50
51
|
* @param {string} text - The query or prompt text
|
|
51
52
|
* @param {Object} options - Per-query overrides: { cost_tolerance, signal, estimated_tokens, blocked_models }
|
|
52
|
-
* @returns {{ model, reason, rule, scores, savings, timestamp }}
|
|
53
|
+
* @returns {{ model, reason, rule, scores, savings, timestamp, blocked }}
|
|
53
54
|
*/
|
|
54
55
|
route(text, options = {}) {
|
|
56
|
+
this._stats.total_queries++;
|
|
57
|
+
|
|
58
|
+
// Hard gate: unauthenticated users blocked after ANON_QUERY_LIMIT queries
|
|
59
|
+
if (!this.config.apiKey && this._stats.total_queries > ANON_QUERY_LIMIT) {
|
|
60
|
+
this._telemetry(text, { model: null, rule: 'unauthenticated_limit', scores: null, savings: null }, 'unauthenticated_limit').catch(() => {});
|
|
61
|
+
return {
|
|
62
|
+
model: null,
|
|
63
|
+
reason: 'Rate limit — get a free API key at orbitai.gtll.app',
|
|
64
|
+
rule: 'unauthenticated_limit',
|
|
65
|
+
signal: null,
|
|
66
|
+
signal_reason: null,
|
|
67
|
+
scores: null,
|
|
68
|
+
savings: null,
|
|
69
|
+
timestamp: new Date().toISOString(),
|
|
70
|
+
blocked: true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
55
74
|
let scores = fingerprint(text);
|
|
56
75
|
|
|
57
76
|
if (options.cost_tolerance) {
|
|
@@ -77,52 +96,53 @@ export class OrbitClient {
|
|
|
77
96
|
scores,
|
|
78
97
|
savings,
|
|
79
98
|
timestamp: new Date().toISOString(),
|
|
99
|
+
blocked: false,
|
|
80
100
|
};
|
|
81
101
|
|
|
82
|
-
// Stats
|
|
83
|
-
this._stats.total_queries++;
|
|
84
102
|
this._stats.total_savings += savings.savings;
|
|
85
103
|
const modelName = decision.model.name;
|
|
86
104
|
this._stats.model_usage[modelName] = (this._stats.model_usage[modelName] || 0) + 1;
|
|
87
105
|
|
|
88
106
|
if (this.config.log) {
|
|
89
107
|
console.log(`[ORBIT] → ${decision.model.name} | ${decision.rule} | saved $${savings.savings.toFixed(5)} (${savings.reductionPct}% vs GPT-4o)`);
|
|
90
|
-
|
|
91
|
-
// Nudge unauthenticated users toward API key
|
|
92
|
-
if (!this.config.apiKey && this._stats.total_queries === 3) {
|
|
93
|
-
console.warn(`[ORBIT] Free tier: 100 API queries/day. Get your key → https://api.gtll.app/orbitSignup (POST { action: "get_key", email })`);
|
|
94
108
|
}
|
|
109
|
+
|
|
110
|
+
// Nudge unauthenticated users at query 5
|
|
111
|
+
if (!this.config.apiKey && this._stats.total_queries === 5) {
|
|
112
|
+
console.warn(`\n\x1b[35m[ORBIT] You've routed 5 queries — unlock full analytics + higher limits.\x1b[0m`);
|
|
113
|
+
console.warn(`\x1b[36m → https://orbitai.gtll.app\x1b[0m\n`);
|
|
114
|
+
this._telemetry(text, result, 'unauthenticated_nudge').catch(() => {});
|
|
95
115
|
}
|
|
96
116
|
|
|
97
117
|
if (this.config.on_route) {
|
|
98
118
|
this.config.on_route(result);
|
|
99
119
|
}
|
|
100
120
|
|
|
101
|
-
// Fire telemetry
|
|
102
|
-
|
|
103
|
-
this._telemetry(text, result).catch(() => {});
|
|
104
|
-
}
|
|
121
|
+
// Fire telemetry on every query (non-blocking, best-effort)
|
|
122
|
+
this._telemetry(text, result).catch(() => {});
|
|
105
123
|
|
|
106
124
|
return result;
|
|
107
125
|
}
|
|
108
126
|
|
|
109
127
|
/**
|
|
110
128
|
* Send a routing decision to the ORBIT gateway for usage tracking.
|
|
111
|
-
*
|
|
129
|
+
* Non-blocking, best-effort.
|
|
112
130
|
* @private
|
|
113
131
|
*/
|
|
114
|
-
async _telemetry(query, decision) {
|
|
132
|
+
async _telemetry(query, decision, event_type = 'sdk_route') {
|
|
115
133
|
try {
|
|
116
134
|
await fetch(GATEWAY_URL, {
|
|
117
135
|
method: 'POST',
|
|
118
136
|
headers: { 'Content-Type': 'application/json' },
|
|
119
137
|
body: JSON.stringify({
|
|
120
|
-
|
|
121
|
-
api_key: this.config.apiKey,
|
|
122
|
-
model_selected: decision.model
|
|
123
|
-
rule: decision.rule,
|
|
124
|
-
signal: decision.scores?.
|
|
125
|
-
savings_pct: decision.savings
|
|
138
|
+
action: event_type,
|
|
139
|
+
api_key: this.config.apiKey || null,
|
|
140
|
+
model_selected: decision.model?.name || null,
|
|
141
|
+
rule: decision.rule || null,
|
|
142
|
+
signal: decision.scores?.signal_applied || null,
|
|
143
|
+
savings_pct: decision.savings?.reductionPct || null,
|
|
144
|
+
total_queries: this._stats.total_queries,
|
|
145
|
+
sdk_version: '0.5.0',
|
|
126
146
|
}),
|
|
127
147
|
});
|
|
128
148
|
} catch (_) {
|
|
@@ -151,7 +171,7 @@ export class OrbitClient {
|
|
|
151
171
|
|
|
152
172
|
/**
|
|
153
173
|
* Default singleton client — zero config, ready to use
|
|
154
|
-
*
|
|
174
|
+
*
|
|
155
175
|
* @example
|
|
156
176
|
* import orbit from '@gabrielsmartin/orbit-sdk'
|
|
157
177
|
* const { model, reason } = orbit.route("write a haiku about recursion")
|