@volter/twin-moonshot 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.
- package/LICENSE +202 -0
- package/README.md +164 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +25 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +86 -0
- package/dist/src/moonshot-budget.d.ts +57 -0
- package/dist/src/moonshot-budget.js +142 -0
- package/dist/src/moonshot-capabilities.d.ts +4 -0
- package/dist/src/moonshot-capabilities.js +1200 -0
- package/dist/src/moonshot-conformance.d.ts +14 -0
- package/dist/src/moonshot-conformance.js +405 -0
- package/dist/src/moonshot-connector.d.ts +168 -0
- package/dist/src/moonshot-connector.js +416 -0
- package/dist/src/moonshot-models.d.ts +36 -0
- package/dist/src/moonshot-models.js +37 -0
- package/dist/src/moonshot-scenario.d.ts +54 -0
- package/dist/src/moonshot-scenario.js +175 -0
- package/dist/src/moonshot-server.d.ts +13 -0
- package/dist/src/moonshot-server.js +202 -0
- package/dist/src/moonshot-stub.d.ts +70 -0
- package/dist/src/moonshot-stub.js +222 -0
- package/dist/src/moonshot-twin.d.ts +144 -0
- package/dist/src/moonshot-twin.js +1647 -0
- package/dist/src/moonshot-types.d.ts +251 -0
- package/dist/src/moonshot-types.js +19 -0
- package/package.json +53 -0
- package/src/cli.ts +25 -0
- package/src/index.ts +129 -0
- package/src/moonshot-budget.ts +163 -0
- package/src/moonshot-capabilities.ts +1220 -0
- package/src/moonshot-conformance.ts +416 -0
- package/src/moonshot-connector.ts +465 -0
- package/src/moonshot-models.ts +89 -0
- package/src/moonshot-scenario.ts +194 -0
- package/src/moonshot-server.ts +220 -0
- package/src/moonshot-stub.ts +230 -0
- package/src/moonshot-twin.ts +1670 -0
- package/src/moonshot-types.ts +225 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// The client-side rate budget — the fail-closed backstop `liveMoonshotExecute` routes every live
|
|
2
|
+
// request through. The MECHANISM is the kernel's shared, vendor-agnostic `RateBudget`; what lives
|
|
3
|
+
// here is Moonshot's DECLARATION (window/ceiling/per-endpoint weights) plus the vendor-bound
|
|
4
|
+
// bindings, exactly as every other pack does.
|
|
5
|
+
//
|
|
6
|
+
// THE GROUNDING (platform.kimi.ai/docs/pricing/limits, read 2026-09-16): Moonshot publishes a
|
|
7
|
+
// per-tier table whose LOWEST published row (Tier 0, deposit ≥ $1) is concurrency 1, RPM 3,
|
|
8
|
+
// TPM 500,000, TPD 1,500,000. RPM 3 is an order of magnitude UNDER the kernel's undeclared
|
|
9
|
+
// fallback (30 calls/min), so the declaration buys resolution DOWNWARD from the fallback, never
|
|
10
|
+
// headroom: the ceiling is pinned at the kernel fallback in units, and the INFERENCE rules price
|
|
11
|
+
// the model-facing endpoints at a weight that keeps the real RPM-3 worst case inside the window.
|
|
12
|
+
// The tier table scales up to Tier 5 (RPM 300), but no caller's tier is knowable here — the
|
|
13
|
+
// budget protects the LOWEST tier by construction, and a higher tier is only ever under-spent.
|
|
14
|
+
// Web-search endpoints (POST /v1/tools/search, /v1/tools/search_pro, /v1/tools/fetch) are
|
|
15
|
+
// metered on their OWN QPS (QPS 1 at Tier 0), independent of the inference RPM — they get their
|
|
16
|
+
// own rule so a search sweep cannot be priced as a cheap read.
|
|
17
|
+
//
|
|
18
|
+
// THE 429 SHAPE (same limits page): a real 429 carries `X-RateLimit-Limit` /
|
|
19
|
+
// `X-RateLimit-Remaining` / `X-RateLimit-Reset` headers. `recordCall` reads the standard
|
|
20
|
+
// `retry-after` backstop plus these.
|
|
21
|
+
import { declareRateBudget, rateBudgetPath, rateBudgetWeight, RateBudget, } from '@volter/world-core';
|
|
22
|
+
const VENDOR = 'moonshot';
|
|
23
|
+
/** Rolling window, in ms. Spend older than this is pruned. */
|
|
24
|
+
export const MOONSHOT_BUDGET_WINDOW_MS = 60_000;
|
|
25
|
+
/**
|
|
26
|
+
* Weighted units allowed inside one window. 60/60s at `defaultWeight` 2 = 30 calls a minute —
|
|
27
|
+
* EXACTLY the kernel's undeclared fallback. Moonshot DOES publish an RPM scalar (3 at Tier 0),
|
|
28
|
+
* but it is a CALL ceiling, not a weight-unit ceiling; pricing every call 2 keeps the fallback
|
|
29
|
+
* shape and lets the inference rules spend the allowance where the vendor actually meters it.
|
|
30
|
+
*/
|
|
31
|
+
export const MOONSHOT_BUDGET_CEILING = 60;
|
|
32
|
+
/** Seconds. A `retry-after` above this means the key is throttled hard — fail loudly, don't sleep. */
|
|
33
|
+
export const MOONSHOT_BUDGET_MAX_RETRY_AFTER_S = 300;
|
|
34
|
+
/** Per-call cost, keyed by `"<METHOD> <path>"`. See the header for what is documented vs. judged. */
|
|
35
|
+
export const MOONSHOT_CALL_WEIGHTS = {
|
|
36
|
+
/** Model-facing inference: /v1/chat/completions, /v1/responses, /anthropic/v1/messages.
|
|
37
|
+
* At 60/6 that is at most 10 inference calls a window — inside Tier 0's RPM 3×window shape
|
|
38
|
+
* for a single burst while leaving room for the interleaved reads a real client issues. */
|
|
39
|
+
inference: 6,
|
|
40
|
+
/** The web-search tool endpoints — Moonshot meters them on their OWN QPS (1 at Tier 0),
|
|
41
|
+
* independent of inference RPM. Priced like inference so a sweep cannot ride the read weight. */
|
|
42
|
+
webSearch: 6,
|
|
43
|
+
/** Everything else: models, files, batches, balance, token counting, signature verify. */
|
|
44
|
+
other: 2,
|
|
45
|
+
};
|
|
46
|
+
/** THE PACK'S DECLARATION — pure data, the only Moonshot-specific thing in the whole budget. */
|
|
47
|
+
export const MOONSHOT_RATE_BUDGET = {
|
|
48
|
+
windowMs: MOONSHOT_BUDGET_WINDOW_MS,
|
|
49
|
+
ceiling: MOONSHOT_BUDGET_CEILING,
|
|
50
|
+
defaultWeight: MOONSHOT_CALL_WEIGHTS.other,
|
|
51
|
+
maxRetryAfterSeconds: MOONSHOT_BUDGET_MAX_RETRY_AFTER_S,
|
|
52
|
+
rules: [
|
|
53
|
+
// Anchored on Moonshot's REAL paths. Inference is token-metered (TPM 500,000 at Tier 0
|
|
54
|
+
// dwarfs RPM 3 as the binding constraint on real traffic), so model-facing POSTs cost 6.
|
|
55
|
+
{ match: '^POST /v1/chat/completions$', weight: MOONSHOT_CALL_WEIGHTS.inference },
|
|
56
|
+
{ match: '^POST /v1/responses$', weight: MOONSHOT_CALL_WEIGHTS.inference },
|
|
57
|
+
{ match: '^POST /anthropic/v1/messages$', weight: MOONSHOT_CALL_WEIGHTS.inference },
|
|
58
|
+
// Web-search tools: own QPS pool at the vendor, priced at inference weight here.
|
|
59
|
+
{ match: '^POST /v1/tools/search$', weight: MOONSHOT_CALL_WEIGHTS.webSearch },
|
|
60
|
+
{ match: '^POST /v1/tools/search_pro$', weight: MOONSHOT_CALL_WEIGHTS.webSearch },
|
|
61
|
+
{ match: '^POST /v1/tools/fetch$', weight: MOONSHOT_CALL_WEIGHTS.webSearch },
|
|
62
|
+
],
|
|
63
|
+
reason: 'Moonshot publishes a per-tier limit table (platform.kimi.ai/docs/pricing/limits, read ' +
|
|
64
|
+
'2026-09-16): Tier 0 (deposit ≥ $1) is concurrency 1, RPM 3, TPM 500,000, TPD 1,500,000, ' +
|
|
65
|
+
'Web Search QPS 1; Tier 5 (≥ $3000) is 100/300/5M/Unlimited/50. A 429 carries ' +
|
|
66
|
+
'X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset. Because the LOWEST published ' +
|
|
67
|
+
'RPM (3) is far under the kernel fallback of 30 calls/min, the ceiling is pinned AT the ' +
|
|
68
|
+
'fallback (60 units / 60s, defaultWeight 2) and the declaration spends it downward: the three ' +
|
|
69
|
+
'model-facing inference endpoints (POST /v1/chat/completions, POST /v1/responses, ' +
|
|
70
|
+
'POST /anthropic/v1/messages) cost 6 each — at most 10 land in a window — because those calls ' +
|
|
71
|
+
'are token-metered (TPM) at the vendor and one call spends far more of a real account\'s ' +
|
|
72
|
+
'allowance than a list poll. The web-search tool endpoints get their own 6-weight rule ' +
|
|
73
|
+
'because Moonshot meters them on a SEPARATE QPS pool (1 at Tier 0), so a search sweep must ' +
|
|
74
|
+
'not be priceable as a cheap read. The per-tier judgment call is the strict direction: the ' +
|
|
75
|
+
'budget protects Tier 0 by construction and only ever under-spends a higher tier. The window ' +
|
|
76
|
+
'bounds the 60s AVERAGE and does not pace; a `retry-after` read off a real 429 is the ' +
|
|
77
|
+
'persisted cooldown backstop.',
|
|
78
|
+
};
|
|
79
|
+
// Declared at module load, so merely importing this module (which `moonshot-connector.ts` does)
|
|
80
|
+
// is enough to arm the real ceiling.
|
|
81
|
+
declareRateBudget(VENDOR, MOONSHOT_RATE_BUDGET);
|
|
82
|
+
/**
|
|
83
|
+
* Price one call. The key is `"<METHOD> <path>"` with the query string split off, so a rule can
|
|
84
|
+
* price by method (a write is not a read) without the kernel knowing anything about Moonshot. An
|
|
85
|
+
* unclassified endpoint still costs `defaultWeight` — nothing is ever free.
|
|
86
|
+
*/
|
|
87
|
+
export function moonshotCallWeight(method, path) {
|
|
88
|
+
const { bare, query } = moonshotSplitQuery(path);
|
|
89
|
+
// UPPER-CASE the method: `fetch` normalizes a known lowercase method before sending, so
|
|
90
|
+
// `execute('post', …)` really does issue a POST and must be priced as one.
|
|
91
|
+
return rateBudgetWeight(VENDOR, `${String(method).toUpperCase()} ${bare}`, query);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* `/v1/chat/completions?a=1` -> `{ bare: '/v1/chat/completions', query: { a: '1' } }`. Rules
|
|
95
|
+
* match the path; NORMALIZED, because the anchored rules are otherwise trivially evaded: `fetch`
|
|
96
|
+
* upper-cases a known method before sending, so `execute('post', …)` issues a real WRITE that a
|
|
97
|
+
* `^POST ` rule would price as a read; and a trailing slash makes a path miss a `$` anchor while
|
|
98
|
+
* most routers treat it as the same endpoint.
|
|
99
|
+
*/
|
|
100
|
+
function moonshotSplitQuery(path) {
|
|
101
|
+
const at = path.indexOf('?');
|
|
102
|
+
const query = {};
|
|
103
|
+
if (at !== -1)
|
|
104
|
+
for (const [k, v] of new URLSearchParams(path.slice(at + 1)))
|
|
105
|
+
query[k] = v;
|
|
106
|
+
// Collapse REPEATED slashes as well as a trailing one: `//v1/chat/completions` reaches the
|
|
107
|
+
// same endpoint on most routers but misses a `^POST /v1/...$` rule, which would price an
|
|
108
|
+
// inference call as a 2-unit read.
|
|
109
|
+
const raw = (at === -1 ? path : path.slice(0, at)).replace(/\/{2,}/g, '/');
|
|
110
|
+
const bare = raw.length > 1 && raw.endsWith('/') ? raw.replace(/\/+$/, '') : raw;
|
|
111
|
+
return { bare, query };
|
|
112
|
+
}
|
|
113
|
+
/** Where Moonshot's ledger lives. Token-keyed and cwd-independent by default (Moonshot's limits
|
|
114
|
+
* are per ACCOUNT, so a cwd-scoped ledger would hand the same key a fresh allowance in every
|
|
115
|
+
* checkout, worktree and CI matrix leg); pass `root` for world-scoped accounting. */
|
|
116
|
+
export function moonshotBudgetPath(opts = {}) {
|
|
117
|
+
const o = typeof opts === 'string' ? { root: opts } : opts;
|
|
118
|
+
// VENDOR spread LAST: a loosely-typed `{ vendor: 'other', … }` slipping through (TypeScript's
|
|
119
|
+
// excess-property check only catches object literals) must not redirect this pack's ledger.
|
|
120
|
+
return rateBudgetPath({ ...o, vendor: VENDOR });
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Moonshot's budget — the shared kernel guard bound to this vendor's declaration. A real
|
|
124
|
+
* subclass, not an alias, so `budget instanceof MoonshotBudget` in `liveMoonshotExecute` means
|
|
125
|
+
* "a budget that accounts against MOONSHOT's ledger under MOONSHOT's ceiling".
|
|
126
|
+
*/
|
|
127
|
+
export class MoonshotBudget extends RateBudget {
|
|
128
|
+
constructor(opts = {}) {
|
|
129
|
+
super({
|
|
130
|
+
...opts,
|
|
131
|
+
// Field-per-line so the mutation gate can neuter the vendor binding: a budget whose
|
|
132
|
+
// `vendor` is not 'moonshot' accounts against another vendor's ledger, and the ceiling
|
|
133
|
+
// verify reads `err.vendor` off the refusal — the neuter must redden THAT assertion.
|
|
134
|
+
vendor: VENDOR,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** The typed refusal. One error class shared with every other vendor's budget; `err.vendor` says
|
|
139
|
+
* which one refused, and `err.kind` says why. */
|
|
140
|
+
export { RateBudgetError as MoonshotBudgetError } from '@volter/world-core';
|
|
141
|
+
/** Re-exported so a caller can age a window out against the DECLARED window, not a hard-coded 60_000. */
|
|
142
|
+
export { MOONSHOT_BUDGET_WINDOW_MS as MOONSHOT_RATE_BUDGET_WINDOW_MS };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type CapabilityReport, type CapabilitySpec } from '@volter/world-tooling';
|
|
2
|
+
export declare const MOONSHOT_CAPABILITIES: CapabilitySpec[];
|
|
3
|
+
export declare const MOONSHOT_AREAS: readonly ["auth", "balance", "batches", "chat", "conformance", "connector", "errors", "files", "messages", "models", "rate_limits", "responses", "signatures", "streaming", "tokens", "tools"];
|
|
4
|
+
export declare function moonshotCapabilities(): Promise<CapabilityReport>;
|