@argosvix/sdk 0.4.2-alpha.1 → 0.4.3-alpha.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.
- package/README.md +81 -0
- package/dist/approvals.d.ts +84 -0
- package/dist/approvals.d.ts.map +1 -0
- package/dist/approvals.js +143 -0
- package/dist/approvals.js.map +1 -0
- package/dist/budgetGate.d.ts +67 -0
- package/dist/budgetGate.d.ts.map +1 -0
- package/dist/budgetGate.js +356 -0
- package/dist/budgetGate.js.map +1 -0
- package/dist/client.js +57 -3
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/policyScan.d.ts +47 -0
- package/dist/policyScan.d.ts.map +1 -0
- package/dist/policyScan.js +192 -0
- package/dist/policyScan.js.map +1 -0
- package/dist/pricing.d.ts +5 -5
- package/dist/pricing.d.ts.map +1 -1
- package/dist/pricing.js +24 -7
- package/dist/pricing.js.map +1 -1
- package/dist/recorder.d.ts +6 -0
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +38 -11
- package/dist/recorder.js.map +1 -1
- package/dist/redaction.d.ts +0 -6
- package/dist/redaction.d.ts.map +1 -1
- package/dist/redaction.js +21 -5
- package/dist/redaction.js.map +1 -1
- package/dist/types.d.ts +59 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +16 -6
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { scanPolicyViolation } from "./policyScan.js";
|
|
2
|
+
/**
|
|
3
|
+
* Runtime 予算ゲート (= ランタイム制御プレーン Phase 1 の SDK 側 enforce)。
|
|
4
|
+
*
|
|
5
|
+
* `config.budgetGate: true` で opt-in すると、wrap 済み client の LLM 呼び出し
|
|
6
|
+
* 前に backend `/v1/gate/budget` の設定 + 当月消費額をローカル評価し、
|
|
7
|
+
* 月次上限超過なら `ArgosvixBudgetExceededError` を投げて呼び出しを止める。
|
|
8
|
+
*
|
|
9
|
+
* 設計 (= R65b fix bundle 反映):
|
|
10
|
+
* - 設定 + 消費額は TTL (= backend response の ttlSeconds、 default 60s)
|
|
11
|
+
* キャッシュ。呼び出し毎の往復はしない。
|
|
12
|
+
* - stale-while-revalidate: snapshot がある間は stale でも即時評価し、
|
|
13
|
+
* refresh は background で走らせる (= hot path に fetch RTT を乗せない)。
|
|
14
|
+
* 同期 await するのは「snapshot が一度も無い初回」と「fail_closed gate が
|
|
15
|
+
* stale」の 2 場面のみ。
|
|
16
|
+
* - 失敗 backoff: fetch 失敗後 FAILURE_RETRY_MS は再試行しない (= backend
|
|
17
|
+
* 障害中に呼び出し毎 3 秒の blocking fetch を発生させない、R65b SB-1)。
|
|
18
|
+
* - TTL 間の消費は recorder 経由のローカル加算で補正する。fetch 成功時は
|
|
19
|
+
* 「fetch 開始時点の補正量」だけ差し引く (= fetch 中に積まれた分を
|
|
20
|
+
* 握り潰さない、R65b M-1)。
|
|
21
|
+
* - 時刻は monotonic clock (performance.now) 基準 (= NTP step / VM resume の
|
|
22
|
+
* 時計逆行で cache が無限 fresh になる事故の防止、R65b M-4)。
|
|
23
|
+
* - fail_open (default) = backend 不達 / 設定未取得時は通す。
|
|
24
|
+
* - fail_closed = backend の gate 設定が fail_closed のとき、 cache が
|
|
25
|
+
* MAX_STALE_MULTIPLIER × TTL を超えて stale なら
|
|
26
|
+
* ArgosvixBudgetGateUnavailableError。初回取得前の挙動は
|
|
27
|
+
* `config.budgetGateFailClosed` (default false) で選ぶ。
|
|
28
|
+
* - ゲートで止めた呼び出しも wrapper 経由で error record として ingest
|
|
29
|
+
* される (= 「何件止めたか」 が dashboard / chat で見える)。
|
|
30
|
+
*
|
|
31
|
+
* 既知の limitation (= Phase 1 時点):
|
|
32
|
+
* - SDK が wrap していない method (= Anthropic `messages.stream()`、 Python の
|
|
33
|
+
* `generate_content_stream` 等) は観測も enforce も対象外。詳細は
|
|
34
|
+
* types.ts の budgetGate doc comment 参照。
|
|
35
|
+
*/
|
|
36
|
+
const DEFAULT_GATE_ENDPOINT = "https://ingest.argosvix.com/v1/gate/config";
|
|
37
|
+
const INGEST_SUFFIX = "/v1/ingest";
|
|
38
|
+
const FETCH_TIMEOUT_MS = 3_000;
|
|
39
|
+
const DEFAULT_TTL_MS = 60_000;
|
|
40
|
+
// fetch 失敗後の最小再試行間隔 (= 障害中の hot path 保護)
|
|
41
|
+
const FAILURE_RETRY_MS = 30_000;
|
|
42
|
+
// cache がこの倍数 × TTL を超えて refresh できていなければ「状態不明」扱い
|
|
43
|
+
const MAX_STALE_MULTIPLIER = 5;
|
|
44
|
+
/**
|
|
45
|
+
* model 名の canonical form (= R72b MEDIUM 3)。 Gemini 系 SDK が保持する
|
|
46
|
+
* "models/gemini-pro" の provider prefix を除去し、 allowlist 比較を
|
|
47
|
+
* TS / Python 両 SDK で同一にする。 export = test + Python 側と並走 verify 用。
|
|
48
|
+
*/
|
|
49
|
+
export function canonicalizeModelName(model) {
|
|
50
|
+
return model.startsWith("models/") ? model.slice("models/".length) : model;
|
|
51
|
+
}
|
|
52
|
+
export class ArgosvixBudgetExceededError extends Error {
|
|
53
|
+
spentUsd;
|
|
54
|
+
limitUsd;
|
|
55
|
+
constructor(spentUsd, limitUsd) {
|
|
56
|
+
super(`[argosvix] budget gate: monthly spend $${spentUsd.toFixed(4)} reached the limit $${limitUsd.toFixed(2)}; call blocked before the provider request`);
|
|
57
|
+
this.name = "ArgosvixBudgetExceededError";
|
|
58
|
+
this.spentUsd = spentUsd;
|
|
59
|
+
this.limitUsd = limitUsd;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export class ArgosvixBudgetGateUnavailableError extends Error {
|
|
63
|
+
constructor() {
|
|
64
|
+
super("[argosvix] runtime gate: gate state is unavailable and enforce mode is fail_closed; call blocked");
|
|
65
|
+
this.name = "ArgosvixBudgetGateUnavailableError";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export class ArgosvixPolicyViolationError extends Error {
|
|
69
|
+
reason;
|
|
70
|
+
detail;
|
|
71
|
+
constructor(reason, detail) {
|
|
72
|
+
super(`[argosvix] policy gate: ${detail}; call blocked before the provider request`);
|
|
73
|
+
this.name = "ArgosvixPolicyViolationError";
|
|
74
|
+
this.reason = reason;
|
|
75
|
+
this.detail = detail;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export class RuntimeGate {
|
|
79
|
+
config;
|
|
80
|
+
snapshot = null;
|
|
81
|
+
inflight = null;
|
|
82
|
+
lastAttemptMs = null;
|
|
83
|
+
lastAttemptFailed = false;
|
|
84
|
+
warnedEndpoint = false;
|
|
85
|
+
warnedNoPolicy = false;
|
|
86
|
+
/** 直近の成功 fetch 以降にこのプロセスが record した消費の補正値。 */
|
|
87
|
+
localSpendUsd = 0;
|
|
88
|
+
constructor(config) {
|
|
89
|
+
this.config = config;
|
|
90
|
+
}
|
|
91
|
+
/** monotonic clock。wall clock の逆行 (NTP step / VM resume) に影響されない。 */
|
|
92
|
+
now() {
|
|
93
|
+
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
94
|
+
}
|
|
95
|
+
/** opt-in + 送信可能な構成のときだけ動く。それ以外は完全 no-op。 */
|
|
96
|
+
get active() {
|
|
97
|
+
return ((this.config.budgetGate === true || this.config.policyGate === true) &&
|
|
98
|
+
this.config.disabled !== true &&
|
|
99
|
+
typeof this.config.apiKey === "string" &&
|
|
100
|
+
this.config.apiKey.length > 0);
|
|
101
|
+
}
|
|
102
|
+
/** recorder.record() から呼ばれる。TTL 窓内のローカル消費補正。 */
|
|
103
|
+
noteSpend(costUsd) {
|
|
104
|
+
if (this.config.budgetGate !== true || !this.active)
|
|
105
|
+
return;
|
|
106
|
+
if (Number.isFinite(costUsd) && costUsd > 0) {
|
|
107
|
+
this.localSpendUsd += costUsd;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* LLM 呼び出し直前の enforce。超過なら throw、それ以外は素通し。
|
|
112
|
+
* backend 障害は enforce mode に従って fail-open / fail-closed。
|
|
113
|
+
*/
|
|
114
|
+
async check(ctx = {}) {
|
|
115
|
+
if (!this.active)
|
|
116
|
+
return;
|
|
117
|
+
const snap = this.snapshot;
|
|
118
|
+
if (!snap) {
|
|
119
|
+
// 初回 (または未取得)。backoff 窓内なら fetch せず素通し判定へ。
|
|
120
|
+
if (this.attemptAllowed())
|
|
121
|
+
await this.refresh();
|
|
122
|
+
}
|
|
123
|
+
else if (this.isStale(snap)) {
|
|
124
|
+
if (this.attemptAllowed()) {
|
|
125
|
+
const p = this.refresh();
|
|
126
|
+
const anyFailClosed = (this.config.budgetGate === true &&
|
|
127
|
+
snap.gate?.enforceMode === "fail_closed") ||
|
|
128
|
+
(this.config.policyGate === true &&
|
|
129
|
+
snap.policy?.enforceMode === "fail_closed");
|
|
130
|
+
if (anyFailClosed) {
|
|
131
|
+
// fail_closed は stale で block し得るため同期 refresh で正確性優先
|
|
132
|
+
await p;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// fail_open は stale-while-revalidate (= hot path に RTT を乗せない)
|
|
136
|
+
p.catch(() => { });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
this.evaluate(ctx);
|
|
141
|
+
}
|
|
142
|
+
evaluate(ctx) {
|
|
143
|
+
const snap = this.snapshot;
|
|
144
|
+
if (!snap) {
|
|
145
|
+
// 一度も取得できていない = server 側 enforce mode が不明。
|
|
146
|
+
// local opt-in (budgetGateFailClosed / policyGateFailClosed) でのみ
|
|
147
|
+
// fail_closed に倒す (= R72b HIGH 1: backend が fail_closed 設定でも
|
|
148
|
+
// cold start では SDK がそれを知れないため、 厳格運用は明示 opt-in)。
|
|
149
|
+
if ((this.config.budgetGateFailClosed === true ||
|
|
150
|
+
this.config.policyGateFailClosed === true) &&
|
|
151
|
+
this.lastAttemptFailed) {
|
|
152
|
+
throw new ArgosvixBudgetGateUnavailableError();
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const ageMs = this.now() - snap.fetchedAtMs;
|
|
157
|
+
const isUnknownStale = ageMs < 0 || ageMs > snap.ttlMs * MAX_STALE_MULTIPLIER;
|
|
158
|
+
if (this.config.budgetGate === true && snap.gate) {
|
|
159
|
+
const gate = snap.gate;
|
|
160
|
+
if (isUnknownStale && gate.enforceMode === "fail_closed") {
|
|
161
|
+
throw new ArgosvixBudgetGateUnavailableError();
|
|
162
|
+
}
|
|
163
|
+
const spent = snap.spentUsd + this.localSpendUsd;
|
|
164
|
+
if (spent >= gate.limitUsd) {
|
|
165
|
+
throw new ArgosvixBudgetExceededError(spent, gate.limitUsd);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (this.config.policyGate === true && snap.policy) {
|
|
169
|
+
const policy = snap.policy;
|
|
170
|
+
if (isUnknownStale && policy.enforceMode === "fail_closed") {
|
|
171
|
+
throw new ArgosvixBudgetGateUnavailableError();
|
|
172
|
+
}
|
|
173
|
+
this.evaluatePolicy(policy, ctx);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/** ポリシーゲートのローカル評価 (= モデル allowlist / PII / secret)。 */
|
|
177
|
+
evaluatePolicy(policy, ctx) {
|
|
178
|
+
const model = ctx.model ??
|
|
179
|
+
(ctx.payload && typeof ctx.payload === "object"
|
|
180
|
+
? ctx.payload.model
|
|
181
|
+
: undefined);
|
|
182
|
+
if (policy.allow) {
|
|
183
|
+
if (typeof model === "string" && model.length > 0) {
|
|
184
|
+
if (!policy.allow.has(canonicalizeModelName(model))) {
|
|
185
|
+
// model 名は payload 由来の任意文字列になり得るため、 error message には
|
|
186
|
+
// 許可 charset のみ + 128 文字 cap で sanitize して載せる (= prompt 等の
|
|
187
|
+
// 平文が error record に混入しない構造防御、 R66b SB-1 系)。
|
|
188
|
+
const safeModel = model.replace(/[^A-Za-z0-9._:/-]/g, "?").slice(0, 128);
|
|
189
|
+
throw new ArgosvixPolicyViolationError("model_not_allowed", `model "${safeModel}" is not in the configured allowlist`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else if (policy.enforceMode === "fail_closed") {
|
|
193
|
+
// QA M-5: allowlist 設定済 + model 解決不能 (= ctx.model も payload.model も
|
|
194
|
+
// 取れない経路) のとき、 fail_open は従来どおり素通し (= false positive 厳禁の
|
|
195
|
+
// 既定) だが、 fail_closed なら「許可リストを照合できない呼び出しは止める」
|
|
196
|
+
// 強制意味論で block する (= 旧実装の no-block 穴を opt-in で塞ぐ)。
|
|
197
|
+
throw new ArgosvixPolicyViolationError("model_not_allowed", "request model could not be resolved to check against the allowlist (fail_closed)");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if ((policy.blockPii || policy.blockSecrets) && ctx.payload !== undefined) {
|
|
201
|
+
const hit = scanPolicyViolation(ctx.payload, {
|
|
202
|
+
pii: policy.blockPii,
|
|
203
|
+
secrets: policy.blockSecrets,
|
|
204
|
+
});
|
|
205
|
+
if (hit) {
|
|
206
|
+
throw new ArgosvixPolicyViolationError(hit.kind === "secret" ? "secret_detected" : "pii_detected", `request payload contains ${hit.kind === "secret" ? "a credential-like token" : "PII"} (${hit.pattern}) at ${hit.path || "payload"} ("${hit.snippet}")`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
isStale(snap) {
|
|
211
|
+
const age = this.now() - snap.fetchedAtMs;
|
|
212
|
+
return age < 0 || age >= snap.ttlMs;
|
|
213
|
+
}
|
|
214
|
+
/** 直近 fetch が失敗していた場合、FAILURE_RETRY_MS 経過まで再試行しない。 */
|
|
215
|
+
attemptAllowed() {
|
|
216
|
+
if (this.lastAttemptMs === null || !this.lastAttemptFailed)
|
|
217
|
+
return true;
|
|
218
|
+
return this.now() - this.lastAttemptMs >= FAILURE_RETRY_MS;
|
|
219
|
+
}
|
|
220
|
+
refresh() {
|
|
221
|
+
if (!this.inflight) {
|
|
222
|
+
this.lastAttemptMs = this.now();
|
|
223
|
+
this.inflight = this.fetchOnce().finally(() => {
|
|
224
|
+
this.inflight = null;
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
return this.inflight;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* gate endpoint の決定。 ingest endpoint を override している場合は同 prefix
|
|
231
|
+
* から導出し、 導出できない形 (= /v1/ingest で終わらない custom URL) なら
|
|
232
|
+
* gate を無効化する (= 他環境向け Bearer key を production に送らない、R65b M-2)。
|
|
233
|
+
*/
|
|
234
|
+
gateEndpoint() {
|
|
235
|
+
if (this.config.gateEndpoint)
|
|
236
|
+
return this.config.gateEndpoint;
|
|
237
|
+
const endpoint = this.config.endpoint;
|
|
238
|
+
if (endpoint) {
|
|
239
|
+
if (endpoint.endsWith(INGEST_SUFFIX)) {
|
|
240
|
+
return endpoint.slice(0, -INGEST_SUFFIX.length) + "/v1/gate/config";
|
|
241
|
+
}
|
|
242
|
+
if (!this.warnedEndpoint) {
|
|
243
|
+
this.warnedEndpoint = true;
|
|
244
|
+
// eslint-disable-next-line no-console
|
|
245
|
+
console.warn("[argosvix] budget gate: cannot derive gate endpoint from custom ingest endpoint; set config.gateEndpoint explicitly. Gate enforcement is disabled.");
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
return DEFAULT_GATE_ENDPOINT;
|
|
250
|
+
}
|
|
251
|
+
async fetchOnce() {
|
|
252
|
+
const url = this.gateEndpoint();
|
|
253
|
+
if (url === null) {
|
|
254
|
+
this.lastAttemptFailed = true;
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
// fetch 中に noteSpend された分を reset で握り潰さないため、開始時点の
|
|
258
|
+
// 補正量を捕捉して成功時に差分減算する (R65b M-1)。
|
|
259
|
+
const localAtStart = this.localSpendUsd;
|
|
260
|
+
try {
|
|
261
|
+
const controller = new AbortController();
|
|
262
|
+
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
263
|
+
let res;
|
|
264
|
+
try {
|
|
265
|
+
res = await fetch(url, {
|
|
266
|
+
method: "GET",
|
|
267
|
+
headers: { Authorization: `Bearer ${this.config.apiKey}` },
|
|
268
|
+
signal: controller.signal,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
clearTimeout(timer);
|
|
273
|
+
}
|
|
274
|
+
if (!res.ok) {
|
|
275
|
+
// body を消費して connection 再利用を阻害しない (R65b L-1)
|
|
276
|
+
await res.arrayBuffer().catch(() => { });
|
|
277
|
+
this.lastAttemptFailed = true;
|
|
278
|
+
return; // 旧 snapshot keep、mode に応じて evaluate() 側で判断
|
|
279
|
+
}
|
|
280
|
+
const raw = (await res.json());
|
|
281
|
+
// /v1/gate/config 形 { budget: {...}, policy: {...}, ttlSeconds } を
|
|
282
|
+
// 第一に、 旧 /v1/gate/budget 形 { gates, spentUsdThisMonth, ttlSeconds }
|
|
283
|
+
// (= gateEndpoint override で budget endpoint を指す構成) も受理する。
|
|
284
|
+
const isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
285
|
+
const root = isObj(raw) ? raw : {};
|
|
286
|
+
const budgetPart = isObj(root["budget"]) ? root["budget"] : root;
|
|
287
|
+
const policyPart = isObj(root["policy"]) ? root["policy"] : null;
|
|
288
|
+
if (this.config.policyGate === true &&
|
|
289
|
+
!("policy" in root) &&
|
|
290
|
+
!this.warnedNoPolicy) {
|
|
291
|
+
// 旧 /v1/gate/budget shape に gateEndpoint を向けた構成では policy が
|
|
292
|
+
// 取得できず silent no-op になるため 1 回だけ告知 (= R66b LOW 3)。
|
|
293
|
+
this.warnedNoPolicy = true;
|
|
294
|
+
// eslint-disable-next-line no-console
|
|
295
|
+
console.warn("[argosvix] policy gate: gate endpoint response has no policy section; point gateEndpoint at /v1/gate/config to enable policy enforcement.");
|
|
296
|
+
}
|
|
297
|
+
const gates = Array.isArray(budgetPart["gates"])
|
|
298
|
+
? budgetPart["gates"]
|
|
299
|
+
: [];
|
|
300
|
+
const accountGate = gates.find((g) => isObj(g) &&
|
|
301
|
+
(g["projectId"] === null || g["projectId"] === undefined) &&
|
|
302
|
+
g["enabled"] === true &&
|
|
303
|
+
typeof g["monthlyLimitUsd"] === "number" &&
|
|
304
|
+
Number.isFinite(g["monthlyLimitUsd"])) ?? null;
|
|
305
|
+
let policy = null;
|
|
306
|
+
if (policyPart && policyPart["enabled"] === true) {
|
|
307
|
+
const allowRaw = policyPart["modelAllowlist"];
|
|
308
|
+
// R72b MEDIUM 3 fix: allowlist 側も candidate 側も canonical form
|
|
309
|
+
// (= 先頭 "models/" を除去) で比較する。 Gemini legacy SDK は model 名を
|
|
310
|
+
// "models/gemini-pro" 形式で保持する実装があり (= Python の model_name)、
|
|
311
|
+
// TS (= 入力値そのまま) と片側だけ block される drift があった。
|
|
312
|
+
const allow = Array.isArray(allowRaw)
|
|
313
|
+
? new Set(allowRaw
|
|
314
|
+
.filter((m) => typeof m === "string")
|
|
315
|
+
.map(canonicalizeModelName))
|
|
316
|
+
: null;
|
|
317
|
+
policy = {
|
|
318
|
+
allow: allow && allow.size > 0 ? allow : null,
|
|
319
|
+
blockPii: policyPart["blockPii"] === true,
|
|
320
|
+
blockSecrets: policyPart["blockSecrets"] === true,
|
|
321
|
+
enforceMode: policyPart["enforceMode"] === "fail_closed" ? "fail_closed" : "fail_open",
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
const ttlSeconds = root["ttlSeconds"] ?? budgetPart["ttlSeconds"];
|
|
325
|
+
const spentRaw = budgetPart["spentUsdThisMonth"];
|
|
326
|
+
this.snapshot = {
|
|
327
|
+
fetchedAtMs: this.now(),
|
|
328
|
+
ttlMs: typeof ttlSeconds === "number" &&
|
|
329
|
+
Number.isFinite(ttlSeconds) &&
|
|
330
|
+
ttlSeconds > 0
|
|
331
|
+
? ttlSeconds * 1000
|
|
332
|
+
: DEFAULT_TTL_MS,
|
|
333
|
+
gate: accountGate
|
|
334
|
+
? {
|
|
335
|
+
limitUsd: accountGate["monthlyLimitUsd"],
|
|
336
|
+
enforceMode: accountGate["enforceMode"] === "fail_closed"
|
|
337
|
+
? "fail_closed"
|
|
338
|
+
: "fail_open",
|
|
339
|
+
}
|
|
340
|
+
: null,
|
|
341
|
+
spentUsd: typeof spentRaw === "number" && Number.isFinite(spentRaw) ? spentRaw : 0,
|
|
342
|
+
policy,
|
|
343
|
+
};
|
|
344
|
+
this.localSpendUsd = Math.max(0, this.localSpendUsd - localAtStart);
|
|
345
|
+
this.lastAttemptFailed = false;
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
// network / timeout / JSON 不正 = 旧 snapshot keep。fail_open なら素通し、
|
|
349
|
+
// fail_closed は evaluate() の stale 判定で止まる。
|
|
350
|
+
this.lastAttemptFailed = true;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// Phase 1 で公開した名前の互換 alias (= budget 専用だった頃の命名)。
|
|
355
|
+
export { RuntimeGate as BudgetGate };
|
|
356
|
+
//# sourceMappingURL=budgetGate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budgetGate.js","sourceRoot":"","sources":["../src/budgetGate.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -375,6 +375,7 @@ function wrapOpenAIChat(client, recorder, config) {
|
|
|
375
375
|
const id = generateId();
|
|
376
376
|
const isStream = requestArgs.stream === true;
|
|
377
377
|
try {
|
|
378
|
+
await recorder.budgetGate.check({ model: requestArgs.model, payload: requestArgs });
|
|
378
379
|
const response = await originalCreate(...args);
|
|
379
380
|
if (isStream) {
|
|
380
381
|
return wrapOpenAIStream(response, recorder, requestArgs, start, id, config);
|
|
@@ -515,9 +516,33 @@ function wrapOpenAIResponses(client, recorder, config) {
|
|
|
515
516
|
// Note: responses API streaming support is deferred to a later phase.
|
|
516
517
|
// eslint-disable-next-line no-console
|
|
517
518
|
console.warn("[argosvix] responses.create with stream:true is not yet observed");
|
|
519
|
+
// gate block も error record として観測する (= R65b H-3、 非 stream 経路の
|
|
520
|
+
// catch と同じ narrative)。
|
|
521
|
+
try {
|
|
522
|
+
await recorder.budgetGate.check({ model: requestArgs.model, payload: requestArgs });
|
|
523
|
+
}
|
|
524
|
+
catch (err) {
|
|
525
|
+
recorder.record({
|
|
526
|
+
id,
|
|
527
|
+
provider: "openai",
|
|
528
|
+
model: requestArgs.model || "unknown",
|
|
529
|
+
promptTokens: 0,
|
|
530
|
+
completionTokens: 0,
|
|
531
|
+
totalTokens: 0,
|
|
532
|
+
costUsd: 0,
|
|
533
|
+
latencyMs: Date.now() - start,
|
|
534
|
+
timestamp: new Date().toISOString(),
|
|
535
|
+
tags: { ...(config.tags ?? {}) },
|
|
536
|
+
...buildTraceMeta(config),
|
|
537
|
+
error: err instanceof Error ? err.message : String(err),
|
|
538
|
+
requestMeta: buildResponsesRequestMeta(requestArgs),
|
|
539
|
+
});
|
|
540
|
+
throw err;
|
|
541
|
+
}
|
|
518
542
|
return originalCreate(...args);
|
|
519
543
|
}
|
|
520
544
|
try {
|
|
545
|
+
await recorder.budgetGate.check({ model: requestArgs.model, payload: requestArgs });
|
|
521
546
|
const response = (await originalCreate(...args));
|
|
522
547
|
const latencyMs = Date.now() - start;
|
|
523
548
|
const model = response.model || requestArgs.model || "unknown";
|
|
@@ -598,6 +623,7 @@ function wrapAnthropic(client, recorder, config) {
|
|
|
598
623
|
const id = generateId();
|
|
599
624
|
const isStream = requestArgs.stream === true;
|
|
600
625
|
try {
|
|
626
|
+
await recorder.budgetGate.check({ model: requestArgs.model, payload: requestArgs });
|
|
601
627
|
const response = await originalCreate(...args);
|
|
602
628
|
if (isStream) {
|
|
603
629
|
return wrapAnthropicStream(response, recorder, requestArgs, start, id, config);
|
|
@@ -731,6 +757,7 @@ function wrapMistral(client, recorder, config) {
|
|
|
731
757
|
const requestArgs = args[0] || {};
|
|
732
758
|
const id = generateId();
|
|
733
759
|
try {
|
|
760
|
+
await recorder.budgetGate.check({ model: requestArgs.model, payload: requestArgs });
|
|
734
761
|
const response = (await originalComplete(...args));
|
|
735
762
|
const latencyMs = Date.now() - start;
|
|
736
763
|
const model = response.model || requestArgs.model || "unknown";
|
|
@@ -780,6 +807,7 @@ function wrapMistral(client, recorder, config) {
|
|
|
780
807
|
const requestArgs = args[0] || {};
|
|
781
808
|
const id = generateId();
|
|
782
809
|
try {
|
|
810
|
+
await recorder.budgetGate.check({ model: requestArgs.model, payload: requestArgs });
|
|
783
811
|
const stream = (await originalStream(...args));
|
|
784
812
|
return wrapMistralStream(stream, recorder, requestArgs, start, id, config);
|
|
785
813
|
}
|
|
@@ -897,6 +925,7 @@ function wrapGeminiLegacyModel(model, modelName, recorder, config) {
|
|
|
897
925
|
const id = generateId();
|
|
898
926
|
const requestArgs = args[0];
|
|
899
927
|
try {
|
|
928
|
+
await recorder.budgetGate.check({ model: modelName, payload: requestArgs });
|
|
900
929
|
const result = (await originalGenerate(...args));
|
|
901
930
|
const usage = result.response?.usageMetadata;
|
|
902
931
|
const promptTokens = usage?.promptTokenCount ?? 0;
|
|
@@ -945,6 +974,7 @@ function wrapGeminiLegacyModel(model, modelName, recorder, config) {
|
|
|
945
974
|
const id = generateId();
|
|
946
975
|
const requestArgs = args[0];
|
|
947
976
|
try {
|
|
977
|
+
await recorder.budgetGate.check({ model: modelName, payload: requestArgs });
|
|
948
978
|
const result = (await originalStream(...args));
|
|
949
979
|
if (!result.stream)
|
|
950
980
|
return result;
|
|
@@ -1033,6 +1063,7 @@ function wrapGeminiNew(client, recorder, config) {
|
|
|
1033
1063
|
const requestArgs = args[0] || {};
|
|
1034
1064
|
const modelName = requestArgs.model || "unknown";
|
|
1035
1065
|
try {
|
|
1066
|
+
await recorder.budgetGate.check({ model: modelName, payload: requestArgs });
|
|
1036
1067
|
const result = (await originalGenerate(...args));
|
|
1037
1068
|
const usage = result.usageMetadata;
|
|
1038
1069
|
const promptTokens = usage?.promptTokenCount ?? 0;
|
|
@@ -1082,6 +1113,7 @@ function wrapGeminiNew(client, recorder, config) {
|
|
|
1082
1113
|
const requestArgs = args[0] || {};
|
|
1083
1114
|
const modelName = requestArgs.model || "unknown";
|
|
1084
1115
|
try {
|
|
1116
|
+
await recorder.budgetGate.check({ model: modelName, payload: requestArgs });
|
|
1085
1117
|
const stream = (await originalStream(...args));
|
|
1086
1118
|
// C-2 + H-3 fix: AsyncGenerator wrap with finally-record + error tied to consumption
|
|
1087
1119
|
return (async function* () {
|
|
@@ -1181,10 +1213,32 @@ function buildGeminiNewRequestMeta(requestArgs) {
|
|
|
1181
1213
|
return meta;
|
|
1182
1214
|
}
|
|
1183
1215
|
/**
|
|
1184
|
-
*
|
|
1185
|
-
*
|
|
1216
|
+
* Time-ordered record ID generator.
|
|
1217
|
+
*
|
|
1218
|
+
* audit round2 M26 fix = 旧実装は `Date.now()` prefix + `Math.random().toString(36).slice(2,10)`
|
|
1219
|
+
* suffix で、 (1) 非暗号乱数 (2) 値次第で suffix が 8 文字未満になり実効エントロピーが
|
|
1220
|
+
* 41bit を大きく下回る、 という二重の弱さがあった。 backend は
|
|
1221
|
+
* `INSERT ... ON CONFLICT(account_id, id) DO NOTHING` で衝突を無音 skip するため、
|
|
1222
|
+
* 衝突した本物の call が課金・quota・観測から消える。 暗号乱数 (crypto.getRandomValues /
|
|
1223
|
+
* randomUUID) で 128bit 級の衝突耐性に引き上げる。 先頭に時刻 prefix を残して時系列順も保つ。
|
|
1186
1224
|
*/
|
|
1187
1225
|
function generateId() {
|
|
1188
|
-
|
|
1226
|
+
const timePrefix = Date.now().toString(36);
|
|
1227
|
+
// globalThis.crypto は Workers / Node 18+ / 近代ブラウザで利用可能。 SDK tsconfig は DOM lib を
|
|
1228
|
+
// 含めないため Crypto 型に依存せず構造的に access する。
|
|
1229
|
+
const c = globalThis.crypto;
|
|
1230
|
+
if (c?.randomUUID) {
|
|
1231
|
+
return `${timePrefix}-${c.randomUUID()}`;
|
|
1232
|
+
}
|
|
1233
|
+
if (c?.getRandomValues) {
|
|
1234
|
+
const bytes = new Uint8Array(16);
|
|
1235
|
+
c.getRandomValues(bytes);
|
|
1236
|
+
let hex = "";
|
|
1237
|
+
for (const b of bytes)
|
|
1238
|
+
hex += b.toString(16).padStart(2, "0");
|
|
1239
|
+
return `${timePrefix}-${hex}`;
|
|
1240
|
+
}
|
|
1241
|
+
// crypto 不在 runtime の最終 fallback (= 旧来挙動より広い乱数 2 連結で衝突確率を下げる)。
|
|
1242
|
+
return `${timePrefix}-${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;
|
|
1189
1243
|
}
|
|
1190
1244
|
//# sourceMappingURL=client.js.map
|