@open-autonomy/sdk 2.3.1 → 2.3.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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/rails.ts +45 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-autonomy/sdk",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
4
4
  "description": "The Open Autonomy SDK: the roadmap model and its codec, the development-stream client (sessions, turns, updates), and the key helpers. Everything it does is a documented HTTP wire any language can speak without it.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/rails.ts CHANGED
@@ -72,21 +72,56 @@ export function parseModelsBound(yaml: string): string[] {
72
72
  return out;
73
73
  }
74
74
 
75
- // The most the project's funds may spend on the model rail in a day (UTC), from the same file:
75
+ // The limits the project's funds run under on the model rail, from the same file the shape a provider's rate
76
+ // limits and a budget's period share: each limit is one measure over one window, rolling.
76
77
  // spend:
77
- // daily_usd_cents: 500
78
- // 0 or absent: no bound of the project's own beyond the platform's global rail.
79
- export function parseSpendBound(yaml: string): { daily_usd_cents: number } {
80
- let block = '';
81
- let daily = 0;
78
+ // limits:
79
+ // - { window: 1d, usd_cents: 500 } # the money: cents over the window
80
+ // - { window: 30d, usd_cents: 6000 }
81
+ // - { window: 1m, calls: 60 } # the pace: requests over the window
82
+ // - { window: 1h, tokens: 2000000 } # the volume: tokens in and out over the window
83
+ // - { window: 1d, usd_cents: 100, model: openai/gpt-5 } # one model's own budget
84
+ // Windows: N m (minutes, up to 60), N h (hours, up to 24), N d (days, up to 31). Every limit holds at once; the platform
85
+ // refuses the call that would cross one and says which. None: no bound of the project's own beyond the platform's rail.
86
+ export interface SpendLimit { window: string; window_seconds: number; usd_cents?: number; calls?: number; tokens?: number; model?: string }
87
+ export function parseWindow(spec: string): number | undefined {
88
+ const m = /^(\d+)\s*(m|h|d)$/.exec(spec.trim());
89
+ if (!m) return undefined;
90
+ const n = Number(m[1]); const unit = { m: 60, h: 3600, d: 86400 }[m[2] as 'm' | 'h' | 'd'];
91
+ const max = { m: 60, h: 24, d: 31 }[m[2] as 'm' | 'h' | 'd'];
92
+ return n >= 1 && n <= max ? n * unit : undefined;
93
+ }
94
+ export function parseSpendLimits(yaml: string): SpendLimit[] {
95
+ const out: SpendLimit[] = [];
96
+ let block = ''; let inLimits = false; let item: Record<string, string> | null = null;
97
+ const flush = () => {
98
+ if (!item) return;
99
+ const seconds = item.window ? parseWindow(item.window) : undefined;
100
+ const num = (k: string) => (item![k] !== undefined && /^\d+$/.test(item![k]) ? Number(item![k]) : undefined);
101
+ const limit: SpendLimit = { window: (item.window ?? '').trim(), window_seconds: seconds ?? 0 };
102
+ if (num('usd_cents') !== undefined) limit.usd_cents = num('usd_cents');
103
+ if (num('calls') !== undefined) limit.calls = num('calls');
104
+ if (num('tokens') !== undefined) limit.tokens = num('tokens');
105
+ if (item.model) limit.model = item.model.trim().replace(/^["']|["']$/g, '');
106
+ if (seconds && (limit.usd_cents !== undefined || limit.calls !== undefined || limit.tokens !== undefined)) out.push(limit);
107
+ item = null;
108
+ };
109
+ const pairs = (text: string): Record<string, string> => Object.fromEntries(text.split(',').map((kv) => kv.split(':')).filter((kv) => kv.length >= 2).map(([k, ...v]) => [k.trim(), v.join(':').trim().replace(/^["']|["']$/g, '')]));
82
110
  for (const raw of yaml.split('\n')) {
83
111
  const line = raw.replace(/\s+#.*$/, '').trimEnd();
84
112
  if (!line.trim() || line.trim().startsWith('#')) continue;
85
113
  const top = /^([a-z_]+):\s*(.*)$/.exec(line);
86
- if (top) { block = top[2] === '' ? top[1] : ''; continue; }
114
+ if (top) { flush(); block = top[2] === '' ? top[1] : ''; inLimits = false; continue; }
87
115
  if (block !== 'spend') continue;
88
- const kv = /^\s+daily_usd_cents:\s*(\d+)\s*$/.exec(line);
89
- if (kv) daily = Math.max(0, Math.floor(Number(kv[1])));
116
+ if (/^ limits:\s*$/.test(line)) { flush(); inLimits = true; continue; }
117
+ if (!inLimits) continue;
118
+ const flow = /^\s+-\s*\{(.*)\}\s*$/.exec(line);
119
+ if (flow) { flush(); item = pairs(flow[1]); flush(); continue; }
120
+ const start = /^\s+-\s+([a-z_]+):\s*(.+)$/.exec(line);
121
+ if (start) { flush(); item = { [start[1]]: start[2].trim() }; continue; }
122
+ const more = item ? /^\s+([a-z_]+):\s*(.+)$/.exec(line) : null;
123
+ if (more) item![more[1]] = more[2].trim();
90
124
  }
91
- return { daily_usd_cents: daily };
125
+ flush();
126
+ return out;
92
127
  }