@gabrielsmartin/orbit-sdk 0.4.3 → 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 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-3-5"
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 | **$9/mo** · founding rate, locked forever | Unlimited |
158
- | Team | **$49/mo** · founding rate, locked forever | Unlimited · 5 seats |
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.4.3",
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",
@@ -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');
@@ -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 > 30 ? 3 : wordCount > 15 ? 1.5 : 0)
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
- scores: QueryScores;
39
- savings: SavingsInfo;
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 to gateway (non-blocking, best-effort)
102
- if (this.config.apiKey) {
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
- * Only fires if apiKey is set. Non-blocking.
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
- query,
121
- api_key: this.config.apiKey,
122
- model_selected: decision.model.name,
123
- rule: decision.rule,
124
- signal: decision.scores?.signal || null,
125
- savings_pct: decision.savings.reductionPct,
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")