omnigateway 0.14.1 → 0.15.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 +36 -1
- package/bin/omni.js +304 -41
- package/gateway.js +570 -44
- package/package.json +1 -1
- package/public/assets/{Chip-D0XSbQUr.js → Chip-C3dIFd8s.js} +1 -1
- package/public/assets/{CopyValue-DAlmkISB.js → CopyValue-DG88CWpN.js} +1 -1
- package/public/assets/{Field-D9V9N-vb.js → Field-EvdVyEqx.js} +25 -6
- package/public/assets/Lamp-CL0i4yaa.js +26 -0
- package/public/assets/{Rack-CDUOJkS2.js → Rack-DMrfn4-H.js} +5 -5
- package/public/assets/{RequestTable-BF5-R27W.js → RequestTable-CtuuMOT2.js} +3 -3
- package/public/assets/{SummaryDeck-DrzTQQo5.js → SummaryDeck-DHsYYpYO.js} +2 -2
- package/public/assets/{Toggle-CdMD58sy.js → Toggle-BTie3NY8.js} +1 -1
- package/public/assets/{TokenBreakdown-BC6nqo1v.js → TokenBreakdown-Bf0zsKcd.js} +1 -1
- package/public/assets/{WindowChart-Bqbl-KFO.js → WindowChart-CaPUJ4GJ.js} +2 -2
- package/public/assets/_app.accounts-xCJhRd32.js +70 -0
- package/public/assets/{_app.console-BplICGMp.js → _app.console-DS37cfIu.js} +2 -2
- package/public/assets/_app.database-BCwEMrEA.js +24 -0
- package/public/assets/_app.index-DW6wain4.js +67 -0
- package/public/assets/_app.keys-CCU2NB3-.js +120 -0
- package/public/assets/_app.logs-CRC5qDK3.js +46 -0
- package/public/assets/_app.models-B4brzRdK.js +148 -0
- package/public/assets/{_app.plugins._pluginId-BduNUc7R.js → _app.plugins._pluginId-CD9KXknf.js} +1 -1
- package/public/assets/_app.settings-NJSb_L2B.js +88 -0
- package/public/assets/_app.usage-BR6a92zI.js +78 -0
- package/public/assets/chevron-down-3tlYNJqZ.js +1 -0
- package/public/assets/chevron-right-kvaJRnfj.js +1 -0
- package/public/assets/client-B9t0CsmA.js +46 -0
- package/public/assets/{dist-BBGxva0Y.js → dist-B8iuo_hI.js} +1 -1
- package/public/assets/index-6rZSWe9j.js +14 -0
- package/public/assets/{login-Dj7MQcIc.js → login-NuwPF5SS.js} +2 -2
- package/public/assets/plus-CVqm87L-.js +1 -0
- package/public/assets/{preload-helper-B7KDmXJ9.js → preload-helper-1xe0b53X.js} +1 -1
- package/public/assets/{rotate-ccw-Do43Argv.js → rotate-ccw-Dionx_MW.js} +1 -1
- package/public/assets/{shared-DDpLUhCp.js → shared-HVgLEGgl.js} +2 -2
- package/public/assets/{trash-00950_yY.js → trash-Bsi60qJc.js} +1 -1
- package/public/index.html +4 -4
- package/public/assets/Lamp-Dk9XrTCg.js +0 -26
- package/public/assets/_app.accounts-C-Y0w4PQ.js +0 -70
- package/public/assets/_app.database-CKoZXAHB.js +0 -24
- package/public/assets/_app.index-BV1kJJjA.js +0 -67
- package/public/assets/_app.keys-TIg-A16v.js +0 -107
- package/public/assets/_app.logs-CdF9dkSn.js +0 -46
- package/public/assets/_app.models-CPb0eKBe.js +0 -148
- package/public/assets/_app.settings-dGgsENlT.js +0 -88
- package/public/assets/_app.usage--5qFtU1W.js +0 -76
- package/public/assets/chevron-down-Bb5ZGvwk.js +0 -1
- package/public/assets/chevron-right-hwF5mD6-.js +0 -1
- package/public/assets/client-Bx3ttI-9.js +0 -39
- package/public/assets/index-Rh35FNqC.js +0 -14
- package/public/assets/plus-BgcHxxIr.js +0 -1
package/bin/omni.js
CHANGED
|
@@ -7751,6 +7751,17 @@ var _006_model_window_index_default = `-- The per-model long-window reads behind
|
|
|
7751
7751
|
CREATE INDEX idx_request_logs_key_model_at ON request_logs (api_key_id, requested_model, at);
|
|
7752
7752
|
`;
|
|
7753
7753
|
|
|
7754
|
+
// packages/store/src/postgres/migrations/007_key_guardrail.sql
|
|
7755
|
+
var _007_key_guardrail_default = `-- The Postgres half of the sqlite migration \`019_key_guardrail.sql\`: an
|
|
7756
|
+
-- optional usage policy on a gateway API key, NULL meaning no guardrail.
|
|
7757
|
+
--
|
|
7758
|
+
-- No backfill: every existing key was minted without a policy. Safe under a
|
|
7759
|
+
-- rolling deploy \u2014 a nullable column and nothing dropped, so a replica on the
|
|
7760
|
+
-- previous image keeps serving, ignoring the field (and therefore not judging
|
|
7761
|
+
-- that key's requests until it is replaced, which README's upgrade notes say).
|
|
7762
|
+
ALTER TABLE api_keys ADD COLUMN guardrail_policy TEXT;
|
|
7763
|
+
`;
|
|
7764
|
+
|
|
7754
7765
|
// packages/store/src/postgres/db.ts
|
|
7755
7766
|
var num = (v) => Number(v);
|
|
7756
7767
|
var numOrNull = (v) => v === null ? null : Number(v);
|
|
@@ -7761,7 +7772,8 @@ var MIGRATIONS = [
|
|
|
7761
7772
|
{ id: 3, sql: _003_key_expiry_default },
|
|
7762
7773
|
{ id: 4, sql: _004_log_keyset_default },
|
|
7763
7774
|
{ id: 5, sql: _005_log_filter_indexes_default },
|
|
7764
|
-
{ id: 6, sql: _006_model_window_index_default }
|
|
7775
|
+
{ id: 6, sql: _006_model_window_index_default },
|
|
7776
|
+
{ id: 7, sql: _007_key_guardrail_default }
|
|
7765
7777
|
];
|
|
7766
7778
|
async function openPg(url) {
|
|
7767
7779
|
const sql = new SQL({ url });
|
|
@@ -7962,6 +7974,9 @@ function resolvePin(target, accounts) {
|
|
|
7962
7974
|
return;
|
|
7963
7975
|
return accounts.find((account) => servesTarget(target, account));
|
|
7964
7976
|
}
|
|
7977
|
+
var GUARDRAIL_POLICY_MAX_CHARS = 4000;
|
|
7978
|
+
var GUARDRAIL_TIMEOUT_MAX_MS = 60000;
|
|
7979
|
+
var GUARDRAIL_HISTORY_TURNS_MAX = 10;
|
|
7965
7980
|
var keyUsable = (key, now) => key.revokedAt === null && (key.expiresAt === null || key.expiresAt > now);
|
|
7966
7981
|
var credentialPastExpiry = (credential, now) => credential.authType === "oauth" && credential.expiresAt !== null && credential.expiresAt <= now;
|
|
7967
7982
|
var credentialExpired = (credential, now) => credentialPastExpiry(credential, now) && !credential.hasRefreshToken;
|
|
@@ -7981,8 +7996,22 @@ var DEFAULT_SETTINGS = {
|
|
|
7981
7996
|
bodyLoggingEnabled: false,
|
|
7982
7997
|
bodyLoggingCaptureStreamChunks: false,
|
|
7983
7998
|
snapshotKeepLatest: 5,
|
|
7984
|
-
snapshotMaxAgeDays: 30
|
|
7999
|
+
snapshotMaxAgeDays: 30,
|
|
8000
|
+
guardrailModel: null,
|
|
8001
|
+
guardrailTimeoutMs: 1e4,
|
|
8002
|
+
guardrailHistoryTurns: 3
|
|
7985
8003
|
};
|
|
8004
|
+
var inRange = (value, max) => typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= max;
|
|
8005
|
+
function readGuardrailSettings(stored) {
|
|
8006
|
+
const model = stored.guardrailModel;
|
|
8007
|
+
const timeout = stored.guardrailTimeoutMs;
|
|
8008
|
+
const turns = stored.guardrailHistoryTurns;
|
|
8009
|
+
return {
|
|
8010
|
+
guardrailModel: typeof model === "string" && model.trim() !== "" ? model : null,
|
|
8011
|
+
guardrailTimeoutMs: inRange(timeout, GUARDRAIL_TIMEOUT_MAX_MS) ? timeout : DEFAULT_SETTINGS.guardrailTimeoutMs,
|
|
8012
|
+
guardrailHistoryTurns: inRange(turns, GUARDRAIL_HISTORY_TURNS_MAX) ? turns : DEFAULT_SETTINGS.guardrailHistoryTurns
|
|
8013
|
+
};
|
|
8014
|
+
}
|
|
7986
8015
|
|
|
7987
8016
|
// packages/store/src/postgres/config.ts
|
|
7988
8017
|
var SETTINGS_KEY = "settings";
|
|
@@ -8046,7 +8075,8 @@ function createConfigRepo(sql, emit) {
|
|
|
8046
8075
|
bodyRetentionDays: Number.isInteger(stored.bodyRetentionDays) && (stored.bodyRetentionDays ?? 0) >= 1 && (stored.bodyRetentionDays ?? 0) <= 3650 ? stored.bodyRetentionDays : DEFAULT_SETTINGS.bodyRetentionDays,
|
|
8047
8076
|
bodyLoggingEnabled: stored.bodyLoggingEnabled === true,
|
|
8048
8077
|
bodyLoggingCaptureStreamChunks: stored.bodyLoggingCaptureStreamChunks === true,
|
|
8049
|
-
weights: knownWeights(stored.weights)
|
|
8078
|
+
weights: knownWeights(stored.weights),
|
|
8079
|
+
...readGuardrailSettings(stored)
|
|
8050
8080
|
};
|
|
8051
8081
|
} catch {
|
|
8052
8082
|
return DEFAULT_SETTINGS;
|
|
@@ -8447,7 +8477,8 @@ var toKey = (r, logger) => ({
|
|
|
8447
8477
|
bodyLoggingOptOut: r.body_logging_opt_out,
|
|
8448
8478
|
createdAt: num(r.created_at),
|
|
8449
8479
|
revokedAt: numOrNull(r.revoked_at),
|
|
8450
|
-
expiresAt: numOrNull(r.expires_at)
|
|
8480
|
+
expiresAt: numOrNull(r.expires_at),
|
|
8481
|
+
guardrailPolicy: r.guardrail_policy
|
|
8451
8482
|
});
|
|
8452
8483
|
function createKeyRepo(sql, logger) {
|
|
8453
8484
|
const one = async (where, arg) => {
|
|
@@ -8465,8 +8496,9 @@ function createKeyRepo(sql, logger) {
|
|
|
8465
8496
|
async create(input) {
|
|
8466
8497
|
const now = Date.now();
|
|
8467
8498
|
await sql.unsafe(`INSERT INTO api_keys (id, label, prefix, hash, model_allowlist, limits,
|
|
8468
|
-
body_logging_opt_out, created_at, revoked_at, expires_at
|
|
8469
|
-
|
|
8499
|
+
body_logging_opt_out, created_at, revoked_at, expires_at,
|
|
8500
|
+
guardrail_policy)
|
|
8501
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,NULL,$9,$10)`, [
|
|
8470
8502
|
input.id,
|
|
8471
8503
|
input.label,
|
|
8472
8504
|
input.prefix,
|
|
@@ -8475,7 +8507,8 @@ function createKeyRepo(sql, logger) {
|
|
|
8475
8507
|
JSON.stringify(parseLimitConfig(input.limits)),
|
|
8476
8508
|
input.bodyLoggingOptOut,
|
|
8477
8509
|
now,
|
|
8478
|
-
input.expiresAt
|
|
8510
|
+
input.expiresAt,
|
|
8511
|
+
input.guardrailPolicy
|
|
8479
8512
|
]);
|
|
8480
8513
|
return { ...input, createdAt: now, revokedAt: null };
|
|
8481
8514
|
},
|
|
@@ -8483,8 +8516,9 @@ function createKeyRepo(sql, logger) {
|
|
|
8483
8516
|
if (row.limits === null)
|
|
8484
8517
|
throw new Error(`api key ${row.id} has unreadable limits`);
|
|
8485
8518
|
await sql.unsafe(`INSERT INTO api_keys (id, label, prefix, hash, model_allowlist, limits,
|
|
8486
|
-
body_logging_opt_out, created_at, revoked_at, expires_at
|
|
8487
|
-
|
|
8519
|
+
body_logging_opt_out, created_at, revoked_at, expires_at,
|
|
8520
|
+
guardrail_policy)
|
|
8521
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)`, [
|
|
8488
8522
|
row.id,
|
|
8489
8523
|
row.label,
|
|
8490
8524
|
row.prefix,
|
|
@@ -8494,7 +8528,8 @@ function createKeyRepo(sql, logger) {
|
|
|
8494
8528
|
row.bodyLoggingOptOut,
|
|
8495
8529
|
row.createdAt,
|
|
8496
8530
|
row.revokedAt,
|
|
8497
|
-
row.expiresAt
|
|
8531
|
+
row.expiresAt,
|
|
8532
|
+
row.guardrailPolicy
|
|
8498
8533
|
]);
|
|
8499
8534
|
},
|
|
8500
8535
|
async setLimits(id, limits) {
|
|
@@ -8512,6 +8547,9 @@ function createKeyRepo(sql, logger) {
|
|
|
8512
8547
|
async setExpiry(id, expiresAt) {
|
|
8513
8548
|
await sql.unsafe("UPDATE api_keys SET expires_at = $1 WHERE id = $2", [expiresAt, id]);
|
|
8514
8549
|
},
|
|
8550
|
+
async setGuardrailPolicy(id, policy) {
|
|
8551
|
+
await sql.unsafe("UPDATE api_keys SET guardrail_policy = $1 WHERE id = $2", [policy, id]);
|
|
8552
|
+
},
|
|
8515
8553
|
async revoke(id) {
|
|
8516
8554
|
await sql.unsafe("UPDATE api_keys SET revoked_at = $1 WHERE id = $2", [Date.now(), id]);
|
|
8517
8555
|
}
|
|
@@ -9281,8 +9319,16 @@ function createUsageRepo(sql, nodeId, dayOffsetMinutes) {
|
|
|
9281
9319
|
const since = daily ? startOfDay(q.since, dayOffsetMinutes) : q.since;
|
|
9282
9320
|
const until = q.until ?? Number.MAX_SAFE_INTEGER;
|
|
9283
9321
|
const timeColumn = daily ? "day" : "at";
|
|
9284
|
-
const
|
|
9285
|
-
|
|
9322
|
+
const bindings = [since, until];
|
|
9323
|
+
let filters = "";
|
|
9324
|
+
if (q.apiKeyId !== undefined) {
|
|
9325
|
+
bindings.push(q.apiKeyId);
|
|
9326
|
+
filters += ` AND api_key_id = $${bindings.length}`;
|
|
9327
|
+
}
|
|
9328
|
+
if (q.credentialId !== undefined) {
|
|
9329
|
+
bindings.push(q.credentialId);
|
|
9330
|
+
filters += ` AND credential_id = $${bindings.length}`;
|
|
9331
|
+
}
|
|
9286
9332
|
const rows = await sql.unsafe(`SELECT ${key} AS key,
|
|
9287
9333
|
${split ?? "NULL"} AS split,
|
|
9288
9334
|
${MEASURES[grain]},
|
|
@@ -9294,7 +9340,7 @@ function createUsageRepo(sql, nodeId, dayOffsetMinutes) {
|
|
|
9294
9340
|
COALESCE(SUM(${daily ? "rtk_applied_requests" : "CASE WHEN rtk_applied THEN 1 ELSE 0 END"}), 0) AS rtk_applied_requests,
|
|
9295
9341
|
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
9296
9342
|
FROM ${daily ? "usage_daily" : "request_logs"}
|
|
9297
|
-
WHERE ${daily ? "" : "state = 'done' AND "}${timeColumn} >= $1 AND ${timeColumn} <= $2${
|
|
9343
|
+
WHERE ${daily ? "" : "state = 'done' AND "}${timeColumn} >= $1 AND ${timeColumn} <= $2${filters}
|
|
9298
9344
|
GROUP BY 1${split === null ? "" : ", 2"}
|
|
9299
9345
|
ORDER BY requests DESC`, bindings);
|
|
9300
9346
|
return rows.map((r) => {
|
|
@@ -9577,7 +9623,8 @@ function createConfigRepo2(db, emit = () => {}) {
|
|
|
9577
9623
|
bodyRetentionDays: Number.isInteger(stored.bodyRetentionDays) && (stored.bodyRetentionDays ?? 0) >= 1 && (stored.bodyRetentionDays ?? 0) <= 3650 ? stored.bodyRetentionDays : DEFAULT_SETTINGS.bodyRetentionDays,
|
|
9578
9624
|
bodyLoggingEnabled: stored.bodyLoggingEnabled === true,
|
|
9579
9625
|
bodyLoggingCaptureStreamChunks: stored.bodyLoggingCaptureStreamChunks === true,
|
|
9580
|
-
weights: knownWeights2(stored.weights)
|
|
9626
|
+
weights: knownWeights2(stored.weights),
|
|
9627
|
+
...readGuardrailSettings(stored)
|
|
9581
9628
|
};
|
|
9582
9629
|
} catch {
|
|
9583
9630
|
return DEFAULT_SETTINGS;
|
|
@@ -10588,6 +10635,24 @@ var _018_model_window_index_default = `-- An index for the per-model long-window
|
|
|
10588
10635
|
CREATE INDEX idx_request_logs_key_model_at ON request_logs (api_key_id, requested_model, at);
|
|
10589
10636
|
`;
|
|
10590
10637
|
|
|
10638
|
+
// packages/store/src/sqlite/migrations/019_key_guardrail.sql
|
|
10639
|
+
var _019_key_guardrail_default = `-- An optional usage policy on a gateway API key: free text the operator wrote
|
|
10640
|
+
-- about what the key may be used for, judged by a classifier model before each
|
|
10641
|
+
-- inference request (\`apps/gateway/src/guardrail/\`).
|
|
10642
|
+
--
|
|
10643
|
+
-- Nullable, and NULL is the only spelling of "no guardrail". No backfill, for
|
|
10644
|
+
-- the reason migration 14 has none: every existing key was minted without a
|
|
10645
|
+
-- policy, and inventing one would start refusing its client on an upgrade the
|
|
10646
|
+
-- operator never connected to it.
|
|
10647
|
+
--
|
|
10648
|
+
-- Its own column rather than a member of \`limits\`. A policy is not a limit, and
|
|
10649
|
+
-- nesting it there would bind it to the \`@omni/ratelimit/catalog\` storage
|
|
10650
|
+
-- contract, which fails closed on any name it does not know. The cost is
|
|
10651
|
+
-- accepted and documented: a rollback to a build without this column ignores
|
|
10652
|
+
-- it and stops judging \u2014 the guardrail fails open across a downgrade.
|
|
10653
|
+
ALTER TABLE api_keys ADD COLUMN guardrail_policy TEXT;
|
|
10654
|
+
`;
|
|
10655
|
+
|
|
10591
10656
|
// packages/store/src/sqlite/db.ts
|
|
10592
10657
|
var MIGRATIONS2 = [
|
|
10593
10658
|
{ id: 1, sql: _001_init_default2 },
|
|
@@ -10607,7 +10672,8 @@ var MIGRATIONS2 = [
|
|
|
10607
10672
|
{ id: 15, sql: _015_log_keyset_default },
|
|
10608
10673
|
{ id: 16, sql: _016_log_filter_indexes_default },
|
|
10609
10674
|
{ id: 17, sql: _017_log_text_search_default },
|
|
10610
|
-
{ id: 18, sql: _018_model_window_index_default }
|
|
10675
|
+
{ id: 18, sql: _018_model_window_index_default },
|
|
10676
|
+
{ id: 19, sql: _019_key_guardrail_default }
|
|
10611
10677
|
];
|
|
10612
10678
|
function statements(sql) {
|
|
10613
10679
|
return sql.split(`
|
|
@@ -10664,7 +10730,8 @@ var toKey2 = (r, logger) => ({
|
|
|
10664
10730
|
bodyLoggingOptOut: r.body_logging_opt_out === 1,
|
|
10665
10731
|
createdAt: r.created_at,
|
|
10666
10732
|
revokedAt: r.revoked_at,
|
|
10667
|
-
expiresAt: r.expires_at
|
|
10733
|
+
expiresAt: r.expires_at,
|
|
10734
|
+
guardrailPolicy: r.guardrail_policy
|
|
10668
10735
|
});
|
|
10669
10736
|
function createKeyRepo2(db, logger = noopLogger) {
|
|
10670
10737
|
return {
|
|
@@ -10682,8 +10749,9 @@ function createKeyRepo2(db, logger = noopLogger) {
|
|
|
10682
10749
|
async create(input) {
|
|
10683
10750
|
const now = Date.now();
|
|
10684
10751
|
db.run(`INSERT INTO api_keys (id, label, prefix, hash, model_allowlist, limits,
|
|
10685
|
-
body_logging_opt_out, created_at, revoked_at, expires_at
|
|
10686
|
-
|
|
10752
|
+
body_logging_opt_out, created_at, revoked_at, expires_at,
|
|
10753
|
+
guardrail_policy)
|
|
10754
|
+
VALUES (?,?,?,?,?,?,?,?,NULL,?,?)`, [
|
|
10687
10755
|
input.id,
|
|
10688
10756
|
input.label,
|
|
10689
10757
|
input.prefix,
|
|
@@ -10692,7 +10760,8 @@ function createKeyRepo2(db, logger = noopLogger) {
|
|
|
10692
10760
|
JSON.stringify(parseLimitConfig(input.limits)),
|
|
10693
10761
|
input.bodyLoggingOptOut ? 1 : 0,
|
|
10694
10762
|
now,
|
|
10695
|
-
input.expiresAt
|
|
10763
|
+
input.expiresAt,
|
|
10764
|
+
input.guardrailPolicy
|
|
10696
10765
|
]);
|
|
10697
10766
|
return { ...input, createdAt: now, revokedAt: null };
|
|
10698
10767
|
},
|
|
@@ -10700,8 +10769,9 @@ function createKeyRepo2(db, logger = noopLogger) {
|
|
|
10700
10769
|
if (row.limits === null)
|
|
10701
10770
|
throw new Error(`api key ${row.id} has unreadable limits`);
|
|
10702
10771
|
db.run(`INSERT INTO api_keys (id, label, prefix, hash, model_allowlist, limits,
|
|
10703
|
-
body_logging_opt_out, created_at, revoked_at, expires_at
|
|
10704
|
-
|
|
10772
|
+
body_logging_opt_out, created_at, revoked_at, expires_at,
|
|
10773
|
+
guardrail_policy)
|
|
10774
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?)`, [
|
|
10705
10775
|
row.id,
|
|
10706
10776
|
row.label,
|
|
10707
10777
|
row.prefix,
|
|
@@ -10711,7 +10781,8 @@ function createKeyRepo2(db, logger = noopLogger) {
|
|
|
10711
10781
|
row.bodyLoggingOptOut ? 1 : 0,
|
|
10712
10782
|
row.createdAt,
|
|
10713
10783
|
row.revokedAt,
|
|
10714
|
-
row.expiresAt
|
|
10784
|
+
row.expiresAt,
|
|
10785
|
+
row.guardrailPolicy
|
|
10715
10786
|
]);
|
|
10716
10787
|
},
|
|
10717
10788
|
async setLimits(id, limits) {
|
|
@@ -10729,6 +10800,9 @@ function createKeyRepo2(db, logger = noopLogger) {
|
|
|
10729
10800
|
async setExpiry(id, expiresAt) {
|
|
10730
10801
|
db.run("UPDATE api_keys SET expires_at = ? WHERE id = ?", [expiresAt, id]);
|
|
10731
10802
|
},
|
|
10803
|
+
async setGuardrailPolicy(id, policy) {
|
|
10804
|
+
db.run("UPDATE api_keys SET guardrail_policy = ? WHERE id = ?", [policy, id]);
|
|
10805
|
+
},
|
|
10732
10806
|
async revoke(id) {
|
|
10733
10807
|
db.run("UPDATE api_keys SET revoked_at = ? WHERE id = ?", [Date.now(), id]);
|
|
10734
10808
|
}
|
|
@@ -11215,9 +11289,16 @@ function createUsageRepo2(db, nodeId, dayOffsetMinutes = hostDayOffsetMinutes())
|
|
|
11215
11289
|
const since = daily ? startOfDay(q.since, dayOffsetMinutes) : q.since;
|
|
11216
11290
|
const until = q.until ?? Number.MAX_SAFE_INTEGER;
|
|
11217
11291
|
const timeColumn = daily ? "day" : "at";
|
|
11218
|
-
const
|
|
11219
|
-
|
|
11220
|
-
|
|
11292
|
+
const bindings = [since, until];
|
|
11293
|
+
let filters = "";
|
|
11294
|
+
if (q.apiKeyId !== undefined) {
|
|
11295
|
+
filters += " AND api_key_id = ?";
|
|
11296
|
+
bindings.push(q.apiKeyId);
|
|
11297
|
+
}
|
|
11298
|
+
if (q.credentialId !== undefined) {
|
|
11299
|
+
filters += " AND credential_id = ?";
|
|
11300
|
+
bindings.push(q.credentialId);
|
|
11301
|
+
}
|
|
11221
11302
|
const rows = db.query(`SELECT ${key} AS key,
|
|
11222
11303
|
${split ?? "NULL"} AS split,
|
|
11223
11304
|
${MEASURES2[grain]},
|
|
@@ -11229,7 +11310,7 @@ function createUsageRepo2(db, nodeId, dayOffsetMinutes = hostDayOffsetMinutes())
|
|
|
11229
11310
|
COALESCE(SUM(${daily ? "rtk_applied_requests" : "CASE WHEN rtk_applied = 1 THEN 1 ELSE 0 END"}), 0) AS rtk_applied_requests,
|
|
11230
11311
|
COALESCE(SUM(cost_usd), 0) AS cost_usd
|
|
11231
11312
|
FROM ${daily ? "usage_daily" : "request_logs"}
|
|
11232
|
-
WHERE ${daily ? "" : "state = 'done' AND "}${timeColumn} >= ? AND ${timeColumn} <= ?${
|
|
11313
|
+
WHERE ${daily ? "" : "state = 'done' AND "}${timeColumn} >= ? AND ${timeColumn} <= ?${filters}
|
|
11233
11314
|
GROUP BY key${split === null ? "" : ", split"}
|
|
11234
11315
|
ORDER BY requests DESC`).all(...bindings);
|
|
11235
11316
|
return rows.map((r) => {
|
|
@@ -11333,6 +11414,7 @@ async function createStore(opts) {
|
|
|
11333
11414
|
setLimits: (id, limits) => handle.keys.setLimits(id, limits),
|
|
11334
11415
|
setModelAllowlist: (id, modelAllowlist) => handle.keys.setModelAllowlist(id, modelAllowlist),
|
|
11335
11416
|
setExpiry: (id, expiresAt) => handle.keys.setExpiry(id, expiresAt),
|
|
11417
|
+
setGuardrailPolicy: (id, policy) => handle.keys.setGuardrailPolicy(id, policy),
|
|
11336
11418
|
revoke: (id) => handle.keys.revoke(id)
|
|
11337
11419
|
},
|
|
11338
11420
|
usage: {
|
|
@@ -20376,18 +20458,21 @@ var modelSchema = object({
|
|
|
20376
20458
|
targets: array(targetSchema).min(1, "a virtual model needs at least one target")
|
|
20377
20459
|
});
|
|
20378
20460
|
var MAX_INSTANT = 8640000000000000;
|
|
20461
|
+
var guardrailPolicySchema = string2().trim().min(1).max(GUARDRAIL_POLICY_MAX_CHARS);
|
|
20379
20462
|
var keyCreateSchema = object({
|
|
20380
20463
|
label: string2().min(1).default("api key"),
|
|
20381
20464
|
modelAllowlist: array(string2().min(1)).nullable().default(null),
|
|
20382
20465
|
limits: limitConfigSchema.default({}),
|
|
20383
20466
|
bodyLoggingOptOut: boolean2().default(false),
|
|
20384
|
-
expiresAt: number2().int().positive().max(MAX_INSTANT).nullable().default(null)
|
|
20467
|
+
expiresAt: number2().int().positive().max(MAX_INSTANT).nullable().default(null),
|
|
20468
|
+
guardrailPolicy: guardrailPolicySchema.nullable().default(null)
|
|
20385
20469
|
}).strict();
|
|
20386
20470
|
var keyLimitsSchema = object({ limits: limitConfigSchema }).strict();
|
|
20387
20471
|
var keyModelsSchema = object({
|
|
20388
20472
|
modelAllowlist: array(string2().min(1)).nullable()
|
|
20389
20473
|
}).strict();
|
|
20390
20474
|
var keyExpirySchema = object({ expiresAt: number2().int().positive().max(MAX_INSTANT).nullable() }).strict();
|
|
20475
|
+
var keyGuardrailSchema = object({ policy: guardrailPolicySchema.nullable() }).strict();
|
|
20391
20476
|
var retentionSchema = object({
|
|
20392
20477
|
keepLatest: number2().int().min(1).max(100),
|
|
20393
20478
|
maxAgeDays: number2().int().min(1).max(3650)
|
|
@@ -20414,7 +20499,10 @@ var settingsSchema = object({
|
|
|
20414
20499
|
bodyLoggingEnabled: boolean2(),
|
|
20415
20500
|
bodyLoggingCaptureStreamChunks: boolean2(),
|
|
20416
20501
|
snapshotKeepLatest: retentionSchema.shape.keepLatest.optional(),
|
|
20417
|
-
snapshotMaxAgeDays: retentionSchema.shape.maxAgeDays.optional()
|
|
20502
|
+
snapshotMaxAgeDays: retentionSchema.shape.maxAgeDays.optional(),
|
|
20503
|
+
guardrailModel: string2().trim().min(1).max(256).nullable().optional(),
|
|
20504
|
+
guardrailTimeoutMs: number2().int().min(1).max(GUARDRAIL_TIMEOUT_MAX_MS).optional(),
|
|
20505
|
+
guardrailHistoryTurns: number2().int().min(1).max(GUARDRAIL_HISTORY_TURNS_MAX).optional()
|
|
20418
20506
|
});
|
|
20419
20507
|
var credentialPatchSchema = object({
|
|
20420
20508
|
label: string2().min(1).optional(),
|
|
@@ -20429,7 +20517,9 @@ var quotaRefreshSchema = discriminatedUnion("kind", [
|
|
|
20429
20517
|
}).strict(),
|
|
20430
20518
|
object({ kind: literal("all") }).strict()
|
|
20431
20519
|
]);
|
|
20432
|
-
var
|
|
20520
|
+
var opaqueIdSchema = (message) => string2().trim().min(1, message).max(64, message).regex(/^[A-Za-z0-9_-]+$/, message);
|
|
20521
|
+
var accountIdSchema = opaqueIdSchema("credentialId must be an account id");
|
|
20522
|
+
var keyIdSchema = opaqueIdSchema("apiKeyId must be a key id");
|
|
20433
20523
|
var redeemResetSchema = object({
|
|
20434
20524
|
credentialId: accountIdSchema,
|
|
20435
20525
|
creditId: string2().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/).optional()
|
|
@@ -20841,6 +20931,50 @@ function score(pairs, input) {
|
|
|
20841
20931
|
};
|
|
20842
20932
|
});
|
|
20843
20933
|
}
|
|
20934
|
+
// packages/router/src/resolve.ts
|
|
20935
|
+
function prefixProviders(providers) {
|
|
20936
|
+
return Object.entries(providers).flatMap(([id, descriptor]) => descriptor.modelPrefixes.map((prefix) => [prefix, id, descriptor])).sort(([a], [b]) => b.length - a.length);
|
|
20937
|
+
}
|
|
20938
|
+
function synthesize(provider, model, descriptor) {
|
|
20939
|
+
const listed = entryPricing(descriptor.catalog, model);
|
|
20940
|
+
const limits = entryLimits(descriptor.catalog, model);
|
|
20941
|
+
const target = {
|
|
20942
|
+
provider,
|
|
20943
|
+
model,
|
|
20944
|
+
tier: 1,
|
|
20945
|
+
weight: 1,
|
|
20946
|
+
costPerMTok: listed === null ? { input: 0, output: 0 } : {
|
|
20947
|
+
input: listed.input,
|
|
20948
|
+
output: listed.output,
|
|
20949
|
+
cacheRead: listed.cacheRead,
|
|
20950
|
+
cacheWrite5m: listed.cacheWrite5m,
|
|
20951
|
+
cacheWrite1h: listed.cacheWrite1h
|
|
20952
|
+
},
|
|
20953
|
+
...limits === null ? {} : { contextWindow: limits.contextWindow, maxOutputTokens: limits.maxOutputTokens },
|
|
20954
|
+
capabilities: descriptor.capabilities
|
|
20955
|
+
};
|
|
20956
|
+
return { id: `${provider}/${model}`, targets: [target], strategy: "score", isAlias: true };
|
|
20957
|
+
}
|
|
20958
|
+
function resolveModel(name, snapshot, providers = PROVIDER_DESCRIPTORS) {
|
|
20959
|
+
const configured = snapshot.models.get(name);
|
|
20960
|
+
if (configured !== undefined)
|
|
20961
|
+
return configured;
|
|
20962
|
+
const sep = name.search(/[/:]/);
|
|
20963
|
+
if (sep > 0) {
|
|
20964
|
+
const prefix = name.slice(0, sep);
|
|
20965
|
+
const rest = name.slice(sep + 1);
|
|
20966
|
+
const descriptor = Object.hasOwn(providers, prefix) ? providers[prefix] : undefined;
|
|
20967
|
+
if (descriptor !== undefined && prefix !== "custom" && rest.length > 0) {
|
|
20968
|
+
return synthesize(prefix, rest, descriptor);
|
|
20969
|
+
}
|
|
20970
|
+
throw new GatewayError("NO_CANDIDATES", `unknown provider "${safeToken(prefix)}" in model "${safeToken(name)}"`);
|
|
20971
|
+
}
|
|
20972
|
+
for (const [prefix, provider, descriptor] of prefixProviders(providers)) {
|
|
20973
|
+
if (name.startsWith(prefix))
|
|
20974
|
+
return synthesize(provider, name, descriptor);
|
|
20975
|
+
}
|
|
20976
|
+
throw new GatewayError("NO_CANDIDATES", `model "${safeToken(name)}" is not a configured virtual model and its provider could not be inferred`);
|
|
20977
|
+
}
|
|
20844
20978
|
|
|
20845
20979
|
// packages/router/src/index.ts
|
|
20846
20980
|
function weightedShuffle(candidates, rand, headroom, health) {
|
|
@@ -21449,6 +21583,22 @@ async function dryRun(deps, modelId, input) {
|
|
|
21449
21583
|
}))
|
|
21450
21584
|
};
|
|
21451
21585
|
}
|
|
21586
|
+
// packages/control/src/guardrail.ts
|
|
21587
|
+
async function guardrailReadiness(store, providers, now = Date.now()) {
|
|
21588
|
+
const keysWithPolicy = (await store.keys.list()).filter((key) => key.revokedAt === null && key.guardrailPolicy !== null).length;
|
|
21589
|
+
const snapshot = await buildSnapshot(store, now);
|
|
21590
|
+
const model = snapshot.settings.guardrailModel;
|
|
21591
|
+
if (keysWithPolicy === 0)
|
|
21592
|
+
return { keysWithPolicy, model, problem: null };
|
|
21593
|
+
if (model === null)
|
|
21594
|
+
return { keysWithPolicy, model, problem: "unconfigured" };
|
|
21595
|
+
try {
|
|
21596
|
+
resolveModel(model, snapshot, providers);
|
|
21597
|
+
return { keysWithPolicy, model, problem: null };
|
|
21598
|
+
} catch {
|
|
21599
|
+
return { keysWithPolicy, model, problem: "unresolved" };
|
|
21600
|
+
}
|
|
21601
|
+
}
|
|
21452
21602
|
// packages/control/src/keys.ts
|
|
21453
21603
|
function configuredWindows(limits) {
|
|
21454
21604
|
const needed = new Set;
|
|
@@ -21518,7 +21668,8 @@ async function toSummary(store, key, now) {
|
|
|
21518
21668
|
bodyLoggingOptOut: key.bodyLoggingOptOut,
|
|
21519
21669
|
createdAt: key.createdAt,
|
|
21520
21670
|
revokedAt: key.revokedAt,
|
|
21521
|
-
expiresAt: key.expiresAt
|
|
21671
|
+
expiresAt: key.expiresAt,
|
|
21672
|
+
guardrailPolicy: key.guardrailPolicy
|
|
21522
21673
|
};
|
|
21523
21674
|
}
|
|
21524
21675
|
async function listKeys(store, now = Date.now()) {
|
|
@@ -21536,7 +21687,8 @@ async function createKey(store, input) {
|
|
|
21536
21687
|
modelAllowlist: body.modelAllowlist,
|
|
21537
21688
|
limits: body.limits,
|
|
21538
21689
|
bodyLoggingOptOut: body.bodyLoggingOptOut,
|
|
21539
|
-
expiresAt: body.expiresAt
|
|
21690
|
+
expiresAt: body.expiresAt,
|
|
21691
|
+
guardrailPolicy: body.guardrailPolicy
|
|
21540
21692
|
});
|
|
21541
21693
|
return { id: created.id, label: created.label, prefix: created.prefix, key: raw };
|
|
21542
21694
|
}
|
|
@@ -21564,6 +21716,14 @@ async function setKeyExpiry(store, id, input, now = Date.now()) {
|
|
|
21564
21716
|
await store.keys.setExpiry(id, body.expiresAt);
|
|
21565
21717
|
return toSummary(store, { ...key, expiresAt: body.expiresAt }, now);
|
|
21566
21718
|
}
|
|
21719
|
+
async function setKeyGuardrail(store, id, input, now = Date.now()) {
|
|
21720
|
+
const body = parseOrThrow(keyGuardrailSchema, input);
|
|
21721
|
+
const key = await store.keys.get(id);
|
|
21722
|
+
if (key === null)
|
|
21723
|
+
throw new GatewayError("BAD_REQUEST", "no such api key");
|
|
21724
|
+
await store.keys.setGuardrailPolicy(id, body.policy);
|
|
21725
|
+
return toSummary(store, { ...key, guardrailPolicy: body.policy }, now);
|
|
21726
|
+
}
|
|
21567
21727
|
async function revokeKey(store, id) {
|
|
21568
21728
|
await store.keys.revoke(id);
|
|
21569
21729
|
}
|
|
@@ -21589,15 +21749,21 @@ async function queryUsage(deps, input, scope = ALL) {
|
|
|
21589
21749
|
throw new GatewayError("BAD_REQUEST", "this dimension is not available on this surface");
|
|
21590
21750
|
}
|
|
21591
21751
|
}
|
|
21752
|
+
if (input.credentialId !== undefined || input.apiKeyId !== undefined) {
|
|
21753
|
+
throw new GatewayError("BAD_REQUEST", "this filter is not available on this surface");
|
|
21754
|
+
}
|
|
21592
21755
|
}
|
|
21756
|
+
const credentialId = input.credentialId === undefined ? undefined : parseOrThrow(accountIdSchema, input.credentialId);
|
|
21757
|
+
const keyFilter = input.apiKeyId === undefined ? undefined : parseOrThrow(keyIdSchema, input.apiKeyId);
|
|
21593
21758
|
if (readsNothing(scope))
|
|
21594
21759
|
return [];
|
|
21595
|
-
const apiKeyId = scopeKey(scope);
|
|
21760
|
+
const apiKeyId = scope.kind === "all" ? keyFilter : scopeKey(scope);
|
|
21596
21761
|
return deps.store.usage.aggregate({
|
|
21597
21762
|
grain,
|
|
21598
21763
|
groupBy,
|
|
21599
21764
|
...splitBy === undefined ? {} : { splitBy },
|
|
21600
21765
|
...apiKeyId === undefined ? {} : { apiKeyId },
|
|
21766
|
+
...credentialId === undefined ? {} : { credentialId },
|
|
21601
21767
|
since: optionalNumber(input.since, 0),
|
|
21602
21768
|
until: optionalNumber(input.until, deps.now())
|
|
21603
21769
|
});
|
|
@@ -23598,12 +23764,23 @@ async function getSettings(store) {
|
|
|
23598
23764
|
return store.config.getSettings();
|
|
23599
23765
|
}
|
|
23600
23766
|
async function putSettings(store, input) {
|
|
23601
|
-
const {
|
|
23767
|
+
const {
|
|
23768
|
+
bodyRetentionDays,
|
|
23769
|
+
snapshotKeepLatest,
|
|
23770
|
+
snapshotMaxAgeDays,
|
|
23771
|
+
guardrailModel,
|
|
23772
|
+
guardrailTimeoutMs,
|
|
23773
|
+
guardrailHistoryTurns,
|
|
23774
|
+
...rest
|
|
23775
|
+
} = parseOrThrow(settingsSchema, input);
|
|
23602
23776
|
const patch = {
|
|
23603
23777
|
...rest,
|
|
23604
23778
|
...bodyRetentionDays === undefined ? {} : { bodyRetentionDays },
|
|
23605
23779
|
...snapshotKeepLatest === undefined ? {} : { snapshotKeepLatest },
|
|
23606
|
-
...snapshotMaxAgeDays === undefined ? {} : { snapshotMaxAgeDays }
|
|
23780
|
+
...snapshotMaxAgeDays === undefined ? {} : { snapshotMaxAgeDays },
|
|
23781
|
+
...guardrailModel === undefined ? {} : { guardrailModel },
|
|
23782
|
+
...guardrailTimeoutMs === undefined ? {} : { guardrailTimeoutMs },
|
|
23783
|
+
...guardrailHistoryTurns === undefined ? {} : { guardrailHistoryTurns }
|
|
23607
23784
|
};
|
|
23608
23785
|
return store.config.putSettings(patch);
|
|
23609
23786
|
}
|
|
@@ -25327,6 +25504,7 @@ var keysList = {
|
|
|
25327
25504
|
{ header: "MODELS" },
|
|
25328
25505
|
{ header: "LIMITS" },
|
|
25329
25506
|
{ header: "BODY CAPTURE" },
|
|
25507
|
+
{ header: "POLICY" },
|
|
25330
25508
|
{ header: "STATE" },
|
|
25331
25509
|
{ header: "EXPIRES" },
|
|
25332
25510
|
{ header: "CREATED" }
|
|
@@ -25337,6 +25515,7 @@ var keysList = {
|
|
|
25337
25515
|
key.modelAllowlist === null ? "all" : key.modelAllowlist.length === 0 ? "none" : key.modelAllowlist.join(","),
|
|
25338
25516
|
key.limits === null ? paint(ctx, "red", "unreadable") : summarizeLimits(key.limits),
|
|
25339
25517
|
key.bodyLoggingOptOut ? "no bodies" : "\u2014",
|
|
25518
|
+
key.guardrailPolicy === null ? "no" : "yes",
|
|
25340
25519
|
((verdict) => state(ctx, verdict === "active", verdict))(keyState(key, now)),
|
|
25341
25520
|
formatTime(key.expiresAt),
|
|
25342
25521
|
formatTime(key.createdAt)
|
|
@@ -25345,7 +25524,7 @@ var keysList = {
|
|
|
25345
25524
|
}
|
|
25346
25525
|
};
|
|
25347
25526
|
var keysCreate = {
|
|
25348
|
-
usage: "keys create [--label L] [--allow <model> ...] [--limit <d>:<w>=N ...] [--model-limit <model>:<d>:<w>=N ...] [--expires <when>] [--no-bodies]",
|
|
25527
|
+
usage: "keys create [--label L] [--allow <model> ...] [--limit <d>:<w>=N ...] [--model-limit <model>:<d>:<w>=N ...] [--expires <when>] [--policy <text>] [--no-bodies]",
|
|
25349
25528
|
summary: "Mint a gateway API key, printed once",
|
|
25350
25529
|
options: {
|
|
25351
25530
|
label: { type: "string" },
|
|
@@ -25353,10 +25532,12 @@ var keysCreate = {
|
|
|
25353
25532
|
limit: { type: "string", multiple: true },
|
|
25354
25533
|
"model-limit": { type: "string", multiple: true },
|
|
25355
25534
|
expires: { type: "string" },
|
|
25535
|
+
policy: { type: "string" },
|
|
25356
25536
|
"no-bodies": { type: "boolean" }
|
|
25357
25537
|
},
|
|
25358
25538
|
async run(args, { ctx, writer }) {
|
|
25359
25539
|
const allow = listFlag(args.values, "allow");
|
|
25540
|
+
const policy = stringFlag(args.values, "policy");
|
|
25360
25541
|
const expires = stringFlag(args.values, "expires");
|
|
25361
25542
|
const limits = emptyLimits();
|
|
25362
25543
|
parseLimitFlags(listFlag(args.values, "limit") ?? [], "--limit", limits.scope);
|
|
@@ -25366,7 +25547,8 @@ var keysCreate = {
|
|
|
25366
25547
|
modelAllowlist: allow ?? null,
|
|
25367
25548
|
limits: toWire2(limits),
|
|
25368
25549
|
bodyLoggingOptOut: boolFlag(args.values, "no-bodies"),
|
|
25369
|
-
expiresAt: expires === undefined ? null : parseWhen("--expires", expires)
|
|
25550
|
+
expiresAt: expires === undefined ? null : parseWhen("--expires", expires),
|
|
25551
|
+
guardrailPolicy: policy ?? null
|
|
25370
25552
|
});
|
|
25371
25553
|
emit(ctx, writer, created, () => {
|
|
25372
25554
|
note(ctx, writer, paint(ctx, "yellow", "this key is shown once and stored only as a hash"));
|
|
@@ -25482,6 +25664,49 @@ expires: ${formatTime(key.expiresAt)}${verdict === "expired" ? ` ${paint(ctx, "r
|
|
|
25482
25664
|
});
|
|
25483
25665
|
}
|
|
25484
25666
|
};
|
|
25667
|
+
var keysGuardrail = {
|
|
25668
|
+
usage: "keys guardrail <id> [--policy <text> | --policy-file <path> | --clear]",
|
|
25669
|
+
summary: "Show or replace one key's usage policy",
|
|
25670
|
+
options: {
|
|
25671
|
+
policy: { type: "string" },
|
|
25672
|
+
"policy-file": { type: "string" },
|
|
25673
|
+
clear: { type: "boolean" }
|
|
25674
|
+
},
|
|
25675
|
+
async run(args, { ctx, writer }) {
|
|
25676
|
+
const id = requirePositional(args, 0, "key id");
|
|
25677
|
+
const text = stringFlag(args.values, "policy");
|
|
25678
|
+
const file = stringFlag(args.values, "policy-file");
|
|
25679
|
+
const clear = boolFlag(args.values, "clear");
|
|
25680
|
+
const given = [text !== undefined, file !== undefined, clear].filter(Boolean).length;
|
|
25681
|
+
if (given > 1) {
|
|
25682
|
+
throw new UsageError("pass one of --policy, --policy-file or --clear");
|
|
25683
|
+
}
|
|
25684
|
+
const store = await ctx.store();
|
|
25685
|
+
const existing = (await listKeys(store)).find((entry) => entry.id === id);
|
|
25686
|
+
if (existing === undefined)
|
|
25687
|
+
throw new CliError(`no api key "${id}"`);
|
|
25688
|
+
let key = existing;
|
|
25689
|
+
if (given === 1) {
|
|
25690
|
+
const policy = clear ? null : file !== undefined ? await Bun.file(file).text().catch(() => {
|
|
25691
|
+
throw new CliError(`could not read ${file}`);
|
|
25692
|
+
}) : text;
|
|
25693
|
+
key = await setKeyGuardrail(store, id, { policy });
|
|
25694
|
+
}
|
|
25695
|
+
emit(ctx, writer, key, () => {
|
|
25696
|
+
const head = fields([
|
|
25697
|
+
["id", key.id],
|
|
25698
|
+
["label", key.label],
|
|
25699
|
+
["prefix", `${key.prefix}\u2026`]
|
|
25700
|
+
]);
|
|
25701
|
+
if (key.guardrailPolicy === null)
|
|
25702
|
+
return `${head}
|
|
25703
|
+
policy: none; requests are not judged`;
|
|
25704
|
+
return `${head}
|
|
25705
|
+
policy:
|
|
25706
|
+
${key.guardrailPolicy}`;
|
|
25707
|
+
});
|
|
25708
|
+
}
|
|
25709
|
+
};
|
|
25485
25710
|
var keysRevoke = {
|
|
25486
25711
|
usage: "keys revoke <id>",
|
|
25487
25712
|
summary: "Revoke a gateway API key, keeping its usage history",
|
|
@@ -26081,7 +26306,7 @@ function createServiceDeps(input) {
|
|
|
26081
26306
|
}
|
|
26082
26307
|
|
|
26083
26308
|
// apps/cli/src/version.ts
|
|
26084
|
-
var VERSION = "0.
|
|
26309
|
+
var VERSION = "0.15.0";
|
|
26085
26310
|
|
|
26086
26311
|
// apps/cli/src/commands/service.ts
|
|
26087
26312
|
function gatewayArgv(root) {
|
|
@@ -26236,6 +26461,30 @@ async function danglingCredentials(ctx, plugins) {
|
|
|
26236
26461
|
return null;
|
|
26237
26462
|
}
|
|
26238
26463
|
}
|
|
26464
|
+
async function guardrailState(ctx) {
|
|
26465
|
+
if (ctx.configError !== null || !existsSync5(ctx.databasePath))
|
|
26466
|
+
return null;
|
|
26467
|
+
try {
|
|
26468
|
+
return await guardrailReadiness(await ctx.store(), PROVIDER_DESCRIPTORS);
|
|
26469
|
+
} catch {
|
|
26470
|
+
return null;
|
|
26471
|
+
}
|
|
26472
|
+
}
|
|
26473
|
+
function guardrailLine(ctx, readiness, plugins) {
|
|
26474
|
+
if (readiness === null)
|
|
26475
|
+
return paint(ctx, "dim", "not checked");
|
|
26476
|
+
const { keysWithPolicy: n, model, problem } = readiness;
|
|
26477
|
+
if (n === 0)
|
|
26478
|
+
return paint(ctx, "dim", "no key carries one");
|
|
26479
|
+
const keys = `${n} key${n === 1 ? "" : "s"}`;
|
|
26480
|
+
if (problem === "unconfigured") {
|
|
26481
|
+
return paint(ctx, "yellow", `${keys} carry a policy and no classifier is set (guardrailModel); their requests are refused`);
|
|
26482
|
+
}
|
|
26483
|
+
if (problem === "unresolved") {
|
|
26484
|
+
return plugins.length > 0 ? paint(ctx, "yellow", `${keys} carry a policy; classifier "${model}" is not a built-in model (plugin providers not checked)`) : paint(ctx, "yellow", `${keys} carry a policy and classifier "${model}" does not resolve; their requests are refused`);
|
|
26485
|
+
}
|
|
26486
|
+
return state(ctx, true, `${keys}, judged by ${model}`);
|
|
26487
|
+
}
|
|
26239
26488
|
var doctor = {
|
|
26240
26489
|
usage: "doctor",
|
|
26241
26490
|
summary: "Check what this CLI resolved, and whether it can do anything with it",
|
|
@@ -26251,6 +26500,7 @@ var doctor = {
|
|
|
26251
26500
|
const pins = await danglingPins(ctx);
|
|
26252
26501
|
const uninstalled = await missingProviders(ctx, plugins);
|
|
26253
26502
|
const strandedKeys = await danglingCredentials(ctx, plugins);
|
|
26503
|
+
const guardrail = await guardrailState(ctx);
|
|
26254
26504
|
const checks = {
|
|
26255
26505
|
version: VERSION,
|
|
26256
26506
|
root: deps.root,
|
|
@@ -26272,6 +26522,7 @@ var doctor = {
|
|
|
26272
26522
|
danglingPins: pins,
|
|
26273
26523
|
missingProviders: uninstalled,
|
|
26274
26524
|
danglingCredentials: strandedKeys,
|
|
26525
|
+
guardrail,
|
|
26275
26526
|
warnings: ctx.warnings
|
|
26276
26527
|
};
|
|
26277
26528
|
emit(ctx, writer, checks, () => {
|
|
@@ -26311,6 +26562,7 @@ ${fields([
|
|
|
26311
26562
|
"stranded credentials",
|
|
26312
26563
|
strandedKeys === null ? paint(ctx, "dim", "not checked") : strandedKeys.length === 0 ? ok(true, "none") : paint(ctx, "yellow", `${strandedKeys.length}: ${strandedKeys.join(", ")}`)
|
|
26313
26564
|
],
|
|
26565
|
+
["usage policies", guardrailLine(ctx, guardrail, plugins)],
|
|
26314
26566
|
...checks.warnings.map((warning) => ["ignored", paint(ctx, "yellow", warning)])
|
|
26315
26567
|
])}`;
|
|
26316
26568
|
});
|
|
@@ -26374,9 +26626,15 @@ function asString2(path, raw) {
|
|
|
26374
26626
|
throw new UsageError(`${path} must not be blank`);
|
|
26375
26627
|
return value;
|
|
26376
26628
|
}
|
|
26629
|
+
function asNullableString(path, raw) {
|
|
26630
|
+
return raw.trim() === "null" ? null : asString2(path, raw);
|
|
26631
|
+
}
|
|
26632
|
+
function nullable2(head) {
|
|
26633
|
+
return has(DEFAULT_SETTINGS, head) && DEFAULT_SETTINGS[head] === null;
|
|
26634
|
+
}
|
|
26377
26635
|
var settingsSet = {
|
|
26378
26636
|
usage: "settings set <path> <value>",
|
|
26379
|
-
summary: "Change one setting, e.g. weights.cost 0.4 or
|
|
26637
|
+
summary: "Change one setting, e.g. weights.cost 0.4, rtkEnabled true, or guardrailModel null",
|
|
26380
26638
|
async run(args, { ctx, writer }) {
|
|
26381
26639
|
const path = requirePositional(args, 0, "setting path");
|
|
26382
26640
|
const raw = requirePositional(args, 1, "value");
|
|
@@ -26389,7 +26647,7 @@ var settingsSet = {
|
|
|
26389
26647
|
throw new UsageError(`"${head}" has no sub-settings`);
|
|
26390
26648
|
}
|
|
26391
26649
|
const existing = currentValue(current, head, tail);
|
|
26392
|
-
const value = typeof existing === "boolean" ? asBoolean(path, raw) : typeof existing === "string" ? asString2(path, raw) : asNumber(path, raw);
|
|
26650
|
+
const value = tail === undefined && nullable2(head) ? asNullableString(path, raw) : typeof existing === "boolean" ? asBoolean(path, raw) : typeof existing === "string" ? asString2(path, raw) : asNumber(path, raw);
|
|
26393
26651
|
const next = tail === undefined ? { ...current, [head]: value } : { ...current, weights: { ...current.weights, [tail]: value } };
|
|
26394
26652
|
const saved = await putSettings(store, next);
|
|
26395
26653
|
emit(ctx, writer, { settings: saved }, () => `${path} = ${value}`);
|
|
@@ -26607,7 +26865,9 @@ var usage = {
|
|
|
26607
26865
|
by: { type: "string" },
|
|
26608
26866
|
split: { type: "string" },
|
|
26609
26867
|
since: { type: "string" },
|
|
26610
|
-
until: { type: "string" }
|
|
26868
|
+
until: { type: "string" },
|
|
26869
|
+
account: { type: "string" },
|
|
26870
|
+
key: { type: "string" }
|
|
26611
26871
|
},
|
|
26612
26872
|
async run(args, { ctx, writer }) {
|
|
26613
26873
|
const until = instant(stringFlag(args.values, "until"), ctx.now());
|
|
@@ -26617,7 +26877,9 @@ var usage = {
|
|
|
26617
26877
|
groupBy: stringFlag(args.values, "by"),
|
|
26618
26878
|
splitBy: stringFlag(args.values, "split"),
|
|
26619
26879
|
since,
|
|
26620
|
-
until
|
|
26880
|
+
until,
|
|
26881
|
+
credentialId: stringFlag(args.values, "account"),
|
|
26882
|
+
apiKeyId: stringFlag(args.values, "key")
|
|
26621
26883
|
});
|
|
26622
26884
|
emit(ctx, writer, { rows }, () => {
|
|
26623
26885
|
if (rows.length === 0)
|
|
@@ -26811,6 +27073,7 @@ var COMMANDS = {
|
|
|
26811
27073
|
"keys limits": keysLimits,
|
|
26812
27074
|
"keys models": keysModels,
|
|
26813
27075
|
"keys expiry": keysExpiry,
|
|
27076
|
+
"keys guardrail": keysGuardrail,
|
|
26814
27077
|
"keys revoke": keysRevoke,
|
|
26815
27078
|
"plugin list": pluginList,
|
|
26816
27079
|
"plugin verify": pluginVerify,
|