@volter/twin-cohere 0.1.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.
Files changed (40) hide show
  1. package/README.md +224 -0
  2. package/defaults/handlers.json +26 -0
  3. package/dist/defaults/handlers.json +26 -0
  4. package/dist/src/cli.d.ts +2 -0
  5. package/dist/src/cli.js +31 -0
  6. package/dist/src/cohere-budget.d.ts +55 -0
  7. package/dist/src/cohere-budget.js +171 -0
  8. package/dist/src/cohere-capabilities.d.ts +14 -0
  9. package/dist/src/cohere-capabilities.js +1852 -0
  10. package/dist/src/cohere-conformance.d.ts +17 -0
  11. package/dist/src/cohere-conformance.js +464 -0
  12. package/dist/src/cohere-connector.d.ts +150 -0
  13. package/dist/src/cohere-connector.js +625 -0
  14. package/dist/src/cohere-models.d.ts +21 -0
  15. package/dist/src/cohere-models.js +73 -0
  16. package/dist/src/cohere-scenario.d.ts +57 -0
  17. package/dist/src/cohere-scenario.js +176 -0
  18. package/dist/src/cohere-server.d.ts +16 -0
  19. package/dist/src/cohere-server.js +184 -0
  20. package/dist/src/cohere-stub.d.ts +119 -0
  21. package/dist/src/cohere-stub.js +321 -0
  22. package/dist/src/cohere-twin.d.ts +82 -0
  23. package/dist/src/cohere-twin.js +1243 -0
  24. package/dist/src/cohere-types.d.ts +226 -0
  25. package/dist/src/cohere-types.js +40 -0
  26. package/dist/src/index.d.ts +15 -0
  27. package/dist/src/index.js +84 -0
  28. package/package.json +71 -0
  29. package/src/cli.ts +30 -0
  30. package/src/cohere-budget.ts +197 -0
  31. package/src/cohere-capabilities.ts +1855 -0
  32. package/src/cohere-conformance.ts +489 -0
  33. package/src/cohere-connector.ts +709 -0
  34. package/src/cohere-models.ts +79 -0
  35. package/src/cohere-scenario.ts +194 -0
  36. package/src/cohere-server.ts +195 -0
  37. package/src/cohere-stub.ts +337 -0
  38. package/src/cohere-twin.ts +1290 -0
  39. package/src/cohere-types.ts +231 -0
  40. package/src/index.ts +159 -0
@@ -0,0 +1,197 @@
1
+ // Cohere's CLIENT-SIDE RATE BUDGET — the pack's DECLARATION (the numbers) plus the thin typed
2
+ // bindings `liveCohereExecute` uses. The MECHANISM — the durable token-keyed ledger, the rolling
3
+ // window, reserve-under-lock, the `Retry-After`/429 cooldown, fail-CLOSED on a corrupt ledger —
4
+ // lives ONCE in the vendor-agnostic kernel (`@volter/world-core` → `rateBudget.ts`). Read that module's
5
+ // header for the full rationale AND for the honest list of what the guard does not guarantee (an
6
+ // injected clock or ledger path still defeats it — it guards carelessness, not malice).
7
+ //
8
+ // ── WHY THIS EXISTS ─────────────────────────────────────────────────────────────────────────
9
+ // A real ~4.5-DAY vendor lockout (Figma, 2026-07-25) happened because raw API calls were made
10
+ // outside the pack's connector — no cache, no batching, no ceiling. Discipline only binds the code
11
+ // that follows it; a BUDGET binds the code that does not.
12
+ //
13
+ // ── HOW THE CEILING WAS CHOSEN ──────────────────────────────────────────────────────────────
14
+ // Cohere DOES publish scalar per-minute limits, per endpoint AND per key class. Live-read from
15
+ // https://docs.cohere.com/docs/rate-limits on 2026-08-31:
16
+ // • Chat: TRIAL key 20 req/min across every Command model; PRODUCTION key 500 req/min for
17
+ // Command A / R+ / R / R7B (the newer A-variants are sales-gated).
18
+ // • Rerank: TRIAL 10 req/min; production 1,000 req/min.
19
+ // • EmbedJob: TRIAL 5 req/min; production 50 req/min.
20
+ // • Audio Transcriptions: TRIAL 5 req/min.
21
+ // • Tokenize: TRIAL 100 req/min; production 2,000 req/min.
22
+ // • Embed (text): 2,000 INPUTS/min on both key classes; Embed (images) 5 inputs/min trial.
23
+ // • Parse and "Default (other)": 500 req/min on both classes.
24
+ //
25
+ // THE TRIAL NUMBERS BIND, and they are LOWER than the kernel's undeclared fallback. That fallback
26
+ // (`DEFAULT_RATE_BUDGET`) is 60 units / 60s at defaultWeight 2 — 30 calls a minute — which already
27
+ // exceeds Cohere's 20/min trial chat limit and is 6x its 5/min EmbedJob limit. A pack must only be
28
+ // MORE PERMISSIVE than the fallback when the live first-party source justifies it; here the source
29
+ // justifies being TIGHTER, so this declaration is tighter.
30
+ //
31
+ // The ceiling is 20 weighted units per 60s. At the default weight of 2 that is 10 calls a minute:
32
+ // half of the tightest limit that applies to a plain call (chat, 20/min trial) and comfortably
33
+ // under the 5/min EmbedJob floor once that endpoint's own weight is applied.
34
+ //
35
+ // The window bounds the 60-second AVERAGE; it does NOT pace (the kernel refuses, it never sleeps).
36
+ // So the honest claim is "bounds the minute and converts the vendor's first 429/`retry-after` into
37
+ // a hard stop", not "refuses before the vendor ever 429s".
38
+ //
39
+ // ── HOW THE WEIGHTS WERE CHOSEN ─────────────────────────────────────────────────────────────
40
+ // Cohere counts REQUESTS per endpoint, and the per-endpoint limits differ by two orders of
41
+ // magnitude, so pricing by endpoint is reproducing the vendor's own scheme rather than proxying
42
+ // it. Each weight below is the fallback weight scaled by how much scarcer that endpoint's trial
43
+ // allowance is than chat's:
44
+ // • `POST /v1/embed-jobs` costs 8 — a 5/min trial allowance, four times scarcer than chat's 20.
45
+ // • `POST /v2/rerank` / `POST /v1/rerank` cost 4 — a 10/min trial allowance, twice as scarce.
46
+ // • `POST /v2/chat` / `POST /v1/chat` cost 2 (the default) — the 20/min anchor.
47
+ // • `POST /v1/tokenize` costs 1 — a 100/min trial allowance, five times more generous.
48
+ //
49
+ // Everything unclassified costs `defaultWeight`; nothing is ever free. WHICH published row binds an
50
+ // unclassified call is a deliberate choice worth stating, because the arithmetic is otherwise
51
+ // uncheckable against the source it cites (§9 round 1, m5): the endpoints this connector actually
52
+ // drives — `GET /v1/{datasets,connectors,embed-jobs}` and the connector CRUD — fall under Cohere's
53
+ // "Default (other)" row at 500/min, NOT under chat's 20/min. They are nonetheless priced at the
54
+ // CHAT anchor, i.e. 25x tighter than the vendor allows. That is deliberate: the per-endpoint
55
+ // figures for this set are not individually published, and the cost asymmetry is stark — a
56
+ // too-tight default costs a refused call plus a one-line declaration, a too-loose one costs days of
57
+ // lockout.
58
+ import {
59
+ declareRateBudget,
60
+ rateBudgetPath,
61
+ rateBudgetWeight,
62
+ RateBudget,
63
+ type RateBudgetDeclaration,
64
+ type RateBudgetOptions,
65
+ type RateBudgetReservation,
66
+ type RateBudgetSnapshot,
67
+ } from '@volter/world-core';
68
+
69
+ const VENDOR = 'cohere';
70
+
71
+ /** Rolling window, in ms. Spend older than this is pruned. */
72
+ export const COHERE_BUDGET_WINDOW_MS = 60_000;
73
+
74
+ /**
75
+ * Weighted units allowed inside one window. 20/60s at the default weight of 2 is 10 calls a
76
+ * minute — HALF Cohere's tightest broadly-applicable published limit (chat, 20 req/min on a trial
77
+ * key) and tighter than the kernel's undeclared fallback, because Cohere's trial limits are below
78
+ * that fallback.
79
+ */
80
+ export const COHERE_BUDGET_CEILING = 20;
81
+
82
+ /** Seconds. A `retry-after` above this means the org is throttled hard — fail loudly, don't sleep. */
83
+ export const COHERE_BUDGET_MAX_RETRY_AFTER_S = 300;
84
+
85
+ /** Per-call cost, keyed by `"<METHOD> <path>"`. See the header for the arithmetic. */
86
+ export const COHERE_CALL_WEIGHTS = {
87
+ /** `POST /v1/embed-jobs` — a 5 req/min trial allowance, the scarcest endpoint this pack drives. */
88
+ embedJob: 8,
89
+ /** `POST /v2/rerank`, `POST /v1/rerank` — a 10 req/min trial allowance. */
90
+ rerank: 4,
91
+ /** Chat and everything unclassified — the 20 req/min trial anchor. */
92
+ other: 2,
93
+ /** `POST /v1/tokenize` — a 100 req/min trial allowance, the most generous endpoint modeled. */
94
+ tokenize: 1,
95
+ } as const;
96
+
97
+ /** THE PACK'S DECLARATION — pure data, the only Cohere-specific thing in the whole budget. */
98
+ export const COHERE_RATE_BUDGET: RateBudgetDeclaration = {
99
+ windowMs: COHERE_BUDGET_WINDOW_MS,
100
+ ceiling: COHERE_BUDGET_CEILING,
101
+ defaultWeight: COHERE_CALL_WEIGHTS.other,
102
+ maxRetryAfterSeconds: COHERE_BUDGET_MAX_RETRY_AFTER_S,
103
+ // Ordered, first match wins. Every pattern is ANCHORED at both ends so a longer path cannot
104
+ // borrow a cheaper endpoint's price.
105
+ rules: [
106
+ { match: '^POST /v1/embed-jobs$', weight: COHERE_CALL_WEIGHTS.embedJob },
107
+ { match: '^POST /v2/rerank$', weight: COHERE_CALL_WEIGHTS.rerank },
108
+ { match: '^POST /v1/rerank$', weight: COHERE_CALL_WEIGHTS.rerank },
109
+ { match: '^POST /v1/tokenize$', weight: COHERE_CALL_WEIGHTS.tokenize },
110
+ ],
111
+ reason:
112
+ 'Cohere publishes scalar per-minute limits per endpoint AND per key class ' +
113
+ '(https://docs.cohere.com/docs/rate-limits, read 2026-08-31): chat 20 req/min on a TRIAL key ' +
114
+ 'and 500 on production; rerank 10 trial / 1,000 production; EmbedJob 5 trial / 50 production; ' +
115
+ 'tokenize 100 trial / 2,000 production; embed metered in inputs (2,000/min); "default (other)" ' +
116
+ '500. The TRIAL numbers bind, and they are BELOW the kernel fallback (30 calls/min), so this ' +
117
+ 'declaration is deliberately TIGHTER than the fallback rather than more permissive: 20 weighted ' +
118
+ 'units / 60s at defaultWeight 2 is 10 calls a minute, half the 20/min trial chat limit. Weights ' +
119
+ 'reproduce the vendor\'s own per-endpoint scarcity — embed-jobs 8 (5/min), rerank 4 (10/min), ' +
120
+ 'chat/default 2 (20/min), tokenize 1 (100/min). UNCLASSIFIED calls take the chat anchor rather ' +
121
+ 'than the "default (other)" row that actually governs them (the connector\'s own endpoints are ' +
122
+ 'in that 500/min bucket), i.e. 25x tighter than the vendor allows — deliberate, because those ' +
123
+ 'per-endpoint figures are not individually published and a too-tight default costs a refused ' +
124
+ 'call while a too-loose one costs days of lockout. The window bounds the 60s AVERAGE and does ' +
125
+ 'not pace; the 429 / retry-after cooldown is the backstop for a shorter enforcement horizon.',
126
+ };
127
+
128
+ // Declared at module load, so merely importing this module (which `cohere-connector.ts` does) is
129
+ // enough to arm the real ceiling. A budget constructed BEFORE this runs falls back to the kernel's
130
+ // DEFAULT_RATE_BUDGET, which here is strictly LOOSER (30 calls/min vs 10), so the ordering genuinely
131
+ // matters — constructing through the subclass below (which imports this module) is what makes it a
132
+ // non-issue in practice.
133
+ declareRateBudget(VENDOR, COHERE_RATE_BUDGET);
134
+
135
+ /**
136
+ * Price one call. The key is `"<METHOD> <path>"` with the query string split off, so a rule can
137
+ * price by method (a write is not a read) without the kernel knowing anything about Cohere.
138
+ * An unclassified endpoint still costs `defaultWeight` — nothing is ever free.
139
+ */
140
+ export function cohereCallWeight(method: string, path: string): number {
141
+ const { bare, query } = splitQuery(path);
142
+ // UPPER-CASE the method: `fetch` normalizes a known lowercase method before sending, so
143
+ // `execute('post', …)` really does issue a POST and must be priced as one.
144
+ return rateBudgetWeight(VENDOR, `${String(method).toUpperCase()} ${bare}`, query);
145
+ }
146
+
147
+ /**
148
+ * `/v1/x?a=1` -> `{ bare: '/v1/x', query: { a: '1' } }`. Rules match the path; `whenQuery*` the
149
+ * query.
150
+ *
151
+ * NORMALIZED, because the anchored rules are otherwise trivially evaded: `fetch` upper-cases a
152
+ * known method before sending, so `execute('post', …)` issues a real WRITE that a `^POST ` rule
153
+ * would otherwise price as a read; and a trailing slash makes a path miss a `$` anchor while most
154
+ * routers treat it as the same endpoint. Both are input variations, not attacks, and either one
155
+ * silently voids the "scarce endpoints are priced up" claim the ceiling rests on.
156
+ */
157
+ function splitQuery(path: string): { bare: string; query: Record<string, string> } {
158
+ const at = path.indexOf('?');
159
+ const query: Record<string, string> = {};
160
+ if (at !== -1) for (const [k, v] of new URLSearchParams(path.slice(at + 1))) query[k] = v;
161
+ const raw = at === -1 ? path : path.slice(0, at);
162
+ const bare = raw.length > 1 && raw.endsWith('/') ? raw.replace(/\/+$/, '') : raw;
163
+ return { bare, query };
164
+ }
165
+
166
+ /** Where Cohere's ledger lives. Token-keyed and cwd-independent by default (the limit is per API
167
+ * key, so a cwd-scoped ledger would hand the same key a fresh allowance in every checkout,
168
+ * worktree and CI matrix leg); pass `root` for world-scoped accounting. */
169
+ export function cohereBudgetPath(opts: { root?: string; token?: string } | string = {}): string {
170
+ const o = typeof opts === 'string' ? { root: opts } : opts;
171
+ // VENDOR spread LAST: a loosely-typed `{ vendor: 'other', … }` slipping through (TypeScript's
172
+ // excess-property check only catches object literals) must not redirect this pack's ledger to
173
+ // another vendor's file.
174
+ return rateBudgetPath({ ...o, vendor: VENDOR });
175
+ }
176
+
177
+ /** Construction options for Cohere's budget. The vendor is fixed; everything else may only TIGHTEN. */
178
+ export type CohereBudgetOptions = Omit<RateBudgetOptions, 'vendor'>;
179
+
180
+ /**
181
+ * Cohere's budget — the shared kernel guard bound to this vendor's declaration. A real subclass,
182
+ * not an alias, so `budget instanceof CohereBudget` in `liveCohereExecute` means "a budget that
183
+ * accounts against COHERE's ledger under COHERE's ceiling": another vendor's `RateBudget` (with
184
+ * its own, possibly larger, ceiling) is NOT assignable there.
185
+ */
186
+ export class CohereBudget extends RateBudget {
187
+ constructor(opts: CohereBudgetOptions = {}) {
188
+ super({ ...opts, vendor: VENDOR });
189
+ }
190
+ }
191
+
192
+ /** The typed refusal. One error class shared with every other vendor's budget; `err.vendor` says
193
+ * which one refused, and `err.kind` says why. */
194
+ export { RateBudgetError as CohereBudgetError } from '@volter/world-core';
195
+ export type { RateBudgetErrorKind as CohereBudgetErrorKind } from '@volter/world-core';
196
+ export type CohereBudgetReservation = RateBudgetReservation;
197
+ export type CohereBudgetSnapshot = RateBudgetSnapshot;