pilotswarm-sdk 0.5.47 → 0.5.48
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/api/src/http-api-transport.js +11 -0
- package/api/src/protocol.js +1 -0
- package/dist/cms-migrations.d.ts.map +1 -1
- package/dist/cms-migrations.js +440 -0
- package/dist/cms-migrations.js.map +1 -1
- package/dist/management-client.d.ts +71 -0
- package/dist/management-client.d.ts.map +1 -1
- package/dist/management-client.js +12 -0
- package/dist/management-client.js.map +1 -1
- package/dist/provider-store.d.ts +24 -0
- package/dist/provider-store.d.ts.map +1 -1
- package/dist/provider-store.js +27 -0
- package/dist/provider-store.js.map +1 -1
- package/dist/provider-tools.d.ts.map +1 -1
- package/dist/provider-tools.js +27 -0
- package/dist/provider-tools.js.map +1 -1
- package/dist/session-proxy.d.ts.map +1 -1
- package/dist/session-proxy.js +9 -5
- package/dist/session-proxy.js.map +1 -1
- package/dist/web/generated-op-methods.d.ts +8 -0
- package/dist/web/generated-op-methods.d.ts.map +1 -1
- package/dist/web/generated-op-methods.js +2 -0
- package/dist/web/generated-op-methods.js.map +1 -1
- package/dist/web/web-management-client.d.ts +4 -0
- package/dist/web/web-management-client.d.ts.map +1 -1
- package/dist/web/web-management-client.js +7 -0
- package/dist/web/web-management-client.js.map +1 -1
- package/package.json +3 -3
- package/plugins/mgmt/agents/token-manager.agent.md +1 -0
|
@@ -431,10 +431,21 @@ export class HttpApiTransport {
|
|
|
431
431
|
listProviders() { return this.api.call("listProviders"); }
|
|
432
432
|
getProviderStatus(names) { return this.api.call("getProviderStatus", { names: providerNamesParam(names) }); }
|
|
433
433
|
getProviderUsageGrid() { return this.api.call("getProviderUsageGrid"); }
|
|
434
|
+
getProviderUsageSummary({ days, providers } = {}) {
|
|
435
|
+
// The wire carries the provider list as one comma-separated query
|
|
436
|
+
// value; the server splits it. Names never contain commas.
|
|
437
|
+
const list = Array.isArray(providers) ? providers.filter(Boolean).join(",") : (providers || undefined);
|
|
438
|
+
return this.api.call("getProviderUsageSummary", { ...(days ? { days } : {}), ...(list ? { providers: list } : {}) });
|
|
439
|
+
}
|
|
434
440
|
createProvider({ name, type, credentials, baseUrl, displayName } = {}) { return this.api.call("createProvider", { name, type, credentials, baseUrl, displayName }); }
|
|
435
441
|
deleteProvider(name) { return this.api.call("deleteProvider", { name }); }
|
|
436
442
|
createMyProvider({ name, type, credentials, baseUrl, displayName } = {}) { return this.api.call("createMyProvider", { name, type, credentials, baseUrl, displayName }); }
|
|
437
443
|
updateMyProviderCredential({ name, credentials } = {}) { return this.api.call("updateMyProviderCredential", { name, credentials }); }
|
|
444
|
+
// Admin-only; the server refuses everyone else. This is the browser's and
|
|
445
|
+
// the CLI's path to the shared rotation — 0.5.47 shipped the op on the
|
|
446
|
+
// HTTP table, the MCP and the agent tools but not here, so the portal's
|
|
447
|
+
// Update Key on a shared row reported "not available on this transport".
|
|
448
|
+
updateSharedProviderCredential({ name, credentials } = {}) { return this.api.call("updateSharedProviderCredential", { name, credentials }); }
|
|
438
449
|
deleteMyProvider(name) { return this.api.call("deleteMyProvider", { name }); }
|
|
439
450
|
clearProviderRoutingDependencies(name) { return this.api.call("clearProviderRoutingDependencies", { name }); }
|
|
440
451
|
setProviderLimit({ name, period, model, tokens } = {}) { return this.api.call("setProviderLimit", { name, period, model: model ?? null, tokens }); }
|
package/api/src/protocol.js
CHANGED
|
@@ -273,6 +273,7 @@ export const OPERATIONS = [
|
|
|
273
273
|
{ name: "clearSystemSessionModel", access: "fleet:admin", method: "DELETE", path: "/management/system-sessions/:agentId/model", params: { agentId: path("agentId") }, summary: "Clear one persistent system-agent model override. [admin]" },
|
|
274
274
|
{ name: "setClusterDefault", access: "fleet:admin", method: "PUT", path: "/management/defaults", params: { provider: body(), model: body(), reasoning: body(), context: body() }, summary: "Deprecated alias for setModelDefault(scope=cluster). [admin]" },
|
|
275
275
|
{ name: "setMyDefault", access: "authed", method: "PUT", path: "/me/default", params: { provider: body(), model: body(), reasoning: body(), context: body() }, summary: "The caller's prefill for new sessions. A null provider clears it." },
|
|
276
|
+
{ name: "getProviderUsageSummary", access: "authed", method: "GET", path: "/providers/usage-summary", params: { days: query("number"), providers: query("string") }, summary: "The cluster summary from the usage ledger: today / week / month token totals with the input, output and cache split, a per-UTC-day series, and the per-model pivot across providers, reasoning efforts and context tiers. `providers` is a comma-separated list of names; absent means all. Admins see the whole cluster (system sessions included); everyone else sees their own turns." },
|
|
276
277
|
{ name: "getProviderUsage", access: "authed", method: "GET", path: "/providers/usage", params: { days: query("number"), mine: query("boolean"), ownerUserId: query("number"), provider: query("string"), model: query("string"), sessionId: query("string"), chargeClass: query("string"), dimension: query("string"), limit: query("number") }, summary: "Where the tokens went: { totals, daily[], breakdown[] } over one filter set. dimension: session | user | provider | model | agent. Non-admins see only their own rows. `mine` narrows to the caller's own spend, resolved server-side — it carries no id, so it cannot name anybody else." },
|
|
277
278
|
{ name: "listPausedSessions", access: "authed", method: "GET", path: "/providers/paused", summary: "Sessions waiting on a limit, allowance, hold, or a provider name that no longer resolves. Admins fleet-wide, everyone else their own." },
|
|
278
279
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cms-migrations.d.ts","sourceRoot":"","sources":["../src/cms-migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"cms-migrations.d.ts","sourceRoot":"","sources":["../src/cms-migrations.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,CAiW/D"}
|
package/dist/cms-migrations.js
CHANGED
|
@@ -347,6 +347,21 @@ export function CMS_MIGRATIONS(schema) {
|
|
|
347
347
|
name: "shared_provider_credential_update",
|
|
348
348
|
sql: migration_0067_shared_provider_credential_update(schema),
|
|
349
349
|
},
|
|
350
|
+
{
|
|
351
|
+
version: "0068",
|
|
352
|
+
name: "provider_usage_summary",
|
|
353
|
+
sql: migration_0068_provider_usage_summary(schema),
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
version: "0069",
|
|
357
|
+
name: "provider_grid_token_split",
|
|
358
|
+
sql: migration_0069_provider_grid_token_split(schema),
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
version: "0070",
|
|
362
|
+
name: "token_total_is_input_plus_output",
|
|
363
|
+
steps: migration_0070_token_total_is_input_plus_output(schema),
|
|
364
|
+
},
|
|
350
365
|
];
|
|
351
366
|
}
|
|
352
367
|
// ─── Migration 0044: ms-space keyset for session paging ──────────
|
|
@@ -13768,4 +13783,429 @@ END;
|
|
|
13768
13783
|
$$ LANGUAGE plpgsql VOLATILE;
|
|
13769
13784
|
`;
|
|
13770
13785
|
}
|
|
13786
|
+
/**
|
|
13787
|
+
* 0068: the cluster summary — every provider at once, pivoted by model.
|
|
13788
|
+
*
|
|
13789
|
+
* The Providers tab answers "how much of THIS provider's limit is used",
|
|
13790
|
+
* one provider at a time, from the meters. This answers the other question:
|
|
13791
|
+
* how many tokens the cluster burned today / this week / this month, per
|
|
13792
|
+
* UTC day for a chart, and per MODEL — folded across providers, reasoning
|
|
13793
|
+
* efforts and context tiers, because a person planning capacity thinks in
|
|
13794
|
+
* models, not in the routes tokens took to reach them.
|
|
13795
|
+
*
|
|
13796
|
+
* Read from the ledger, not the meters. The meters count only 'user'
|
|
13797
|
+
* charges (system sessions are exempt from limits by design), so a summary
|
|
13798
|
+
* built on them would silently omit the sweeper, the token manager and
|
|
13799
|
+
* every other system agent — on a real cluster the larger half. The ledger
|
|
13800
|
+
* has every turn; `classes` says how the total splits so nobody has to
|
|
13801
|
+
* wonder why this number is bigger than the Providers tab's.
|
|
13802
|
+
*
|
|
13803
|
+
* Scope: an admin sees the cluster; anyone else sees their own turns. The
|
|
13804
|
+
* provider filter is a plain list of names — the portal turns "Shared" and
|
|
13805
|
+
* "Users" into names before asking, so the database never has to guess
|
|
13806
|
+
* what a preset meant.
|
|
13807
|
+
*/
|
|
13808
|
+
function migration_0068_provider_usage_summary(schema) {
|
|
13809
|
+
const s = schema;
|
|
13810
|
+
return `
|
|
13811
|
+
-- The rows every aggregate below reads: one place for the scope and the
|
|
13812
|
+
-- provider filter, so the KPIs, the chart and the model table can never
|
|
13813
|
+
-- disagree about which turns were counted.
|
|
13814
|
+
CREATE OR REPLACE FUNCTION ${s}.cms_provider_usage_summary_rows(
|
|
13815
|
+
p_viewer BIGINT, p_is_admin BOOLEAN, p_since TIMESTAMPTZ, p_providers TEXT[]
|
|
13816
|
+
) RETURNS SETOF ${s}.provider_usage_ledger AS $$
|
|
13817
|
+
SELECT l.*
|
|
13818
|
+
FROM ${s}.provider_usage_ledger l
|
|
13819
|
+
WHERE l.created_at >= p_since
|
|
13820
|
+
AND (COALESCE(p_is_admin, FALSE) OR l.owner_user_id IS NOT DISTINCT FROM p_viewer)
|
|
13821
|
+
AND (p_providers IS NULL OR cardinality(p_providers) = 0 OR l.provider_name = ANY(p_providers));
|
|
13822
|
+
$$ LANGUAGE sql STABLE;
|
|
13823
|
+
|
|
13824
|
+
CREATE OR REPLACE FUNCTION ${s}.cms_provider_usage_summary_window(
|
|
13825
|
+
p_viewer BIGINT, p_is_admin BOOLEAN, p_since TIMESTAMPTZ, p_providers TEXT[]
|
|
13826
|
+
) RETURNS JSONB AS $$
|
|
13827
|
+
SELECT jsonb_build_object(
|
|
13828
|
+
'input', COALESCE(sum(r.tokens_input), 0),
|
|
13829
|
+
'output', COALESCE(sum(r.tokens_output), 0),
|
|
13830
|
+
'cacheRead', COALESCE(sum(r.tokens_cache_read), 0),
|
|
13831
|
+
'cacheWrite', COALESCE(sum(r.tokens_cache_write), 0),
|
|
13832
|
+
'total', COALESCE(sum(r.tokens_total), 0),
|
|
13833
|
+
'turns', count(*),
|
|
13834
|
+
'sessions', count(DISTINCT r.session_id))
|
|
13835
|
+
FROM ${s}.cms_provider_usage_summary_rows(p_viewer, p_is_admin, p_since, p_providers) r;
|
|
13836
|
+
$$ LANGUAGE sql STABLE;
|
|
13837
|
+
|
|
13838
|
+
CREATE OR REPLACE FUNCTION ${s}.cms_provider_usage_summary(
|
|
13839
|
+
p_viewer BIGINT, p_is_admin BOOLEAN, p_days INTEGER, p_providers TEXT[]
|
|
13840
|
+
) RETURNS JSONB AS $$
|
|
13841
|
+
DECLARE
|
|
13842
|
+
v_days INTEGER := LEAST(GREATEST(COALESCE(p_days, 14), 1), 365);
|
|
13843
|
+
-- UTC days, today included: the ledger stores UTC and the meters' day /
|
|
13844
|
+
-- week / month windows are UTC, so "today" here is the same today.
|
|
13845
|
+
v_today DATE := (now() AT TIME ZONE 'UTC')::date;
|
|
13846
|
+
v_from TIMESTAMPTZ := ((v_today - (v_days - 1)) :: timestamp) AT TIME ZONE 'UTC';
|
|
13847
|
+
v_month TIMESTAMPTZ := ((v_today - 29) :: timestamp) AT TIME ZONE 'UTC';
|
|
13848
|
+
v_since TIMESTAMPTZ := LEAST(v_from, v_month);
|
|
13849
|
+
v_windows JSONB;
|
|
13850
|
+
v_daily JSONB;
|
|
13851
|
+
v_models JSONB;
|
|
13852
|
+
v_classes JSONB;
|
|
13853
|
+
BEGIN
|
|
13854
|
+
-- Today / last 7 UTC days / last 30 UTC days, each with the four-way
|
|
13855
|
+
-- split. The month window is what the ledger is read for at minimum.
|
|
13856
|
+
SELECT jsonb_build_object(
|
|
13857
|
+
'day', ${s}.cms_provider_usage_summary_window(p_viewer, p_is_admin, ((v_today)::timestamp) AT TIME ZONE 'UTC', p_providers),
|
|
13858
|
+
'week', ${s}.cms_provider_usage_summary_window(p_viewer, p_is_admin, ((v_today - 6)::timestamp) AT TIME ZONE 'UTC', p_providers),
|
|
13859
|
+
'month', ${s}.cms_provider_usage_summary_window(p_viewer, p_is_admin, v_month, p_providers))
|
|
13860
|
+
INTO v_windows;
|
|
13861
|
+
|
|
13862
|
+
SELECT COALESCE(jsonb_agg(jsonb_build_object(
|
|
13863
|
+
'day', to_char(d.day, 'YYYY-MM-DD'),
|
|
13864
|
+
'input', d.i, 'output', d.o, 'cacheRead', d.cr, 'cacheWrite', d.cw,
|
|
13865
|
+
'total', d.t, 'turns', d.n) ORDER BY d.day), '[]'::jsonb)
|
|
13866
|
+
INTO v_daily
|
|
13867
|
+
FROM (
|
|
13868
|
+
SELECT (r.created_at AT TIME ZONE 'UTC')::date AS day,
|
|
13869
|
+
sum(r.tokens_input) i, sum(r.tokens_output) o,
|
|
13870
|
+
sum(r.tokens_cache_read) cr, sum(r.tokens_cache_write) cw,
|
|
13871
|
+
sum(r.tokens_total) t, count(*) n
|
|
13872
|
+
FROM ${s}.cms_provider_usage_summary_rows(p_viewer, p_is_admin, v_from, p_providers) r
|
|
13873
|
+
GROUP BY 1) d;
|
|
13874
|
+
|
|
13875
|
+
-- The pivot: the model NAME, whatever provider carried it. model_qualified
|
|
13876
|
+
-- is provider:model, and a model name never holds a colon, so the part
|
|
13877
|
+
-- after the first colon is the model. Each row carries its own per-day
|
|
13878
|
+
-- totals for a sparkline.
|
|
13879
|
+
SELECT COALESCE(jsonb_agg(jsonb_build_object(
|
|
13880
|
+
'model', m.model, 'providers', m.providers, 'turns', m.n,
|
|
13881
|
+
'input', m.i, 'output', m.o, 'cacheRead', m.cr, 'cacheWrite', m.cw, 'total', m.t,
|
|
13882
|
+
'daily', m.daily) ORDER BY m.t DESC, m.model), '[]'::jsonb)
|
|
13883
|
+
INTO v_models
|
|
13884
|
+
FROM (
|
|
13885
|
+
SELECT g.model, count(DISTINCT g.provider_name) providers, count(*) n,
|
|
13886
|
+
sum(g.tokens_input) i, sum(g.tokens_output) o,
|
|
13887
|
+
sum(g.tokens_cache_read) cr, sum(g.tokens_cache_write) cw, sum(g.tokens_total) t,
|
|
13888
|
+
(SELECT COALESCE(jsonb_agg(jsonb_build_object('day', to_char(dd.day, 'YYYY-MM-DD'), 'total', dd.t) ORDER BY dd.day), '[]'::jsonb)
|
|
13889
|
+
FROM (SELECT (x.created_at AT TIME ZONE 'UTC')::date AS day, sum(x.tokens_total) t
|
|
13890
|
+
FROM ${s}.cms_provider_usage_summary_rows(p_viewer, p_is_admin, v_from, p_providers) x
|
|
13891
|
+
WHERE substr(x.model_qualified, position(':' IN x.model_qualified) + 1) = g.model
|
|
13892
|
+
GROUP BY 1) dd) AS daily
|
|
13893
|
+
FROM (SELECT r.*, substr(r.model_qualified, position(':' IN r.model_qualified) + 1) AS model
|
|
13894
|
+
FROM ${s}.cms_provider_usage_summary_rows(p_viewer, p_is_admin, v_from, p_providers) r) g
|
|
13895
|
+
GROUP BY g.model) m;
|
|
13896
|
+
|
|
13897
|
+
-- How the window's total splits by who was charged: people, the
|
|
13898
|
+
-- machinery PilotSwarm runs for itself, or turns with no provider.
|
|
13899
|
+
SELECT COALESCE(jsonb_agg(jsonb_build_object('chargeClass', c.charge_class, 'total', c.t, 'turns', c.n) ORDER BY c.t DESC), '[]'::jsonb)
|
|
13900
|
+
INTO v_classes
|
|
13901
|
+
FROM (SELECT r.charge_class, sum(r.tokens_total) t, count(*) n
|
|
13902
|
+
FROM ${s}.cms_provider_usage_summary_rows(p_viewer, p_is_admin, v_from, p_providers) r
|
|
13903
|
+
GROUP BY 1) c;
|
|
13904
|
+
|
|
13905
|
+
RETURN jsonb_build_object(
|
|
13906
|
+
'days', v_days,
|
|
13907
|
+
'today', to_char(v_today, 'YYYY-MM-DD'),
|
|
13908
|
+
'scope', CASE WHEN COALESCE(p_is_admin, FALSE) THEN 'cluster' ELSE 'mine' END,
|
|
13909
|
+
'windows', v_windows,
|
|
13910
|
+
'daily', v_daily,
|
|
13911
|
+
'models', v_models,
|
|
13912
|
+
'classes', v_classes);
|
|
13913
|
+
END;
|
|
13914
|
+
$$ LANGUAGE plpgsql STABLE;
|
|
13915
|
+
|
|
13916
|
+
`;
|
|
13917
|
+
}
|
|
13918
|
+
/**
|
|
13919
|
+
* 0069: the four-way token split on the Providers page.
|
|
13920
|
+
*
|
|
13921
|
+
* The grid's period cells carried one number per pair (used / quota, yours /
|
|
13922
|
+
* your share) because the METERS hold one number: a limit is on the total,
|
|
13923
|
+
* and the total is input + output + cache read + cache write
|
|
13924
|
+
* (cms_provider_settle_turn, v_total). What went into that total was only
|
|
13925
|
+
* visible on the Cluster summary. Each cell now also carries the split, for
|
|
13926
|
+
* everyone and for the viewer, read from the ledger over the SAME window
|
|
13927
|
+
* and scope the meter covers and over the same 'user' turns the meter
|
|
13928
|
+
* counts — so the four parts add up to the number beside them.
|
|
13929
|
+
*
|
|
13930
|
+
* The per-day series under a selected provider gets the same split, which
|
|
13931
|
+
* widens its RETURNS TABLE, hence DROP + CREATE with the same arguments.
|
|
13932
|
+
*/
|
|
13933
|
+
function migration_0069_provider_grid_token_split(schema) {
|
|
13934
|
+
const s = schema;
|
|
13935
|
+
return `
|
|
13936
|
+
CREATE OR REPLACE FUNCTION ${s}.cms_provider_usage_grid(
|
|
13937
|
+
p_viewer BIGINT, p_is_admin BOOLEAN
|
|
13938
|
+
) RETURNS TABLE(
|
|
13939
|
+
provider_name TEXT, row_kind TEXT, scope TEXT, class TEXT,
|
|
13940
|
+
allowance_pct SMALLINT, hold_until_utc TIMESTAMPTZ, hold_indefinite BOOLEAN,
|
|
13941
|
+
model_row_count INTEGER, owned_by_me BOOLEAN, manageable BOOLEAN,
|
|
13942
|
+
owner_label TEXT, periods JSONB
|
|
13943
|
+
) AS $$
|
|
13944
|
+
WITH windows AS (
|
|
13945
|
+
SELECT per.period, wb.window_start, wb.resets_at, wb.window_key
|
|
13946
|
+
FROM (VALUES ('day'),('week'),('month')) AS per(period)
|
|
13947
|
+
CROSS JOIN LATERAL ${s}.cms_provider_window_bounds(per.period, now()) wb
|
|
13948
|
+
),
|
|
13949
|
+
model_scopes AS (
|
|
13950
|
+
SELECT r.provider_name, r.model_qualified AS scope
|
|
13951
|
+
FROM ${s}.provider_budget_rules r
|
|
13952
|
+
WHERE r.model_qualified IS NOT NULL
|
|
13953
|
+
UNION
|
|
13954
|
+
SELECT m.provider_name, m.scope
|
|
13955
|
+
FROM ${s}.provider_meters m
|
|
13956
|
+
JOIN windows w ON w.period = m.period AND w.window_key = m.window_key_utc
|
|
13957
|
+
WHERE m.scope <> '*'
|
|
13958
|
+
),
|
|
13959
|
+
visible AS (
|
|
13960
|
+
SELECT pi.name, pi.class, pi.allowance_pct, pi.hold_until_utc, pi.hold_indefinite,
|
|
13961
|
+
pi.created_at AS named_at,
|
|
13962
|
+
(pi.class = 'shared' OR pi.owner_user_id IS NOT DISTINCT FROM p_viewer) AS owned_by_me,
|
|
13963
|
+
(CASE WHEN pi.class = 'shared' THEN COALESCE(p_is_admin, FALSE)
|
|
13964
|
+
ELSE pi.owner_user_id IS NOT DISTINCT FROM p_viewer END) AS manageable,
|
|
13965
|
+
(CASE WHEN pi.class = 'shared' OR pi.owner_user_id IS NULL THEN NULL
|
|
13966
|
+
ELSE COALESCE(
|
|
13967
|
+
NULLIF(BTRIM(u.display_name), ''),
|
|
13968
|
+
NULLIF(BTRIM(u.email), ''),
|
|
13969
|
+
'user ' || pi.owner_user_id::text) END) AS owner_label,
|
|
13970
|
+
(SELECT count(*)::INTEGER FROM model_scopes ms WHERE ms.provider_name = pi.name) AS model_rows
|
|
13971
|
+
FROM ${s}.provider_instances pi
|
|
13972
|
+
LEFT JOIN ${s}.users u ON u.user_id = pi.owner_user_id
|
|
13973
|
+
WHERE pi.class = 'shared' OR pi.owner_user_id = p_viewer OR COALESCE(p_is_admin, FALSE)
|
|
13974
|
+
),
|
|
13975
|
+
grid_rows AS (
|
|
13976
|
+
SELECT v.*, 'provider'::TEXT AS row_kind, '*'::TEXT AS scope FROM visible v
|
|
13977
|
+
UNION ALL
|
|
13978
|
+
SELECT v.*, 'model'::TEXT, ms.scope
|
|
13979
|
+
FROM visible v
|
|
13980
|
+
JOIN model_scopes ms ON ms.provider_name = v.name
|
|
13981
|
+
)
|
|
13982
|
+
SELECT g.name, g.row_kind, g.scope, g.class, g.allowance_pct,
|
|
13983
|
+
g.hold_until_utc, g.hold_indefinite,
|
|
13984
|
+
CASE WHEN g.row_kind = 'provider' THEN g.model_rows ELSE 0 END,
|
|
13985
|
+
g.owned_by_me, g.manageable,
|
|
13986
|
+
CASE WHEN g.row_kind = 'provider' THEN g.owner_label ELSE NULL END,
|
|
13987
|
+
jsonb_object_agg(w.period, jsonb_build_object(
|
|
13988
|
+
'ruleId', r.rule_id,
|
|
13989
|
+
'quotaTokens', r.limit_tokens,
|
|
13990
|
+
'usedTokens', COALESCE(m.used_tokens, 0),
|
|
13991
|
+
'yourQuotaTokens', CASE
|
|
13992
|
+
WHEN r.limit_tokens IS NULL OR p_viewer IS NULL THEN NULL
|
|
13993
|
+
WHEN NOT g.owned_by_me THEN NULL
|
|
13994
|
+
WHEN g.allowance_pct < 100
|
|
13995
|
+
THEN ${s}.cms_provider_ceiling(r.limit_tokens, g.allowance_pct)
|
|
13996
|
+
ELSE r.limit_tokens END,
|
|
13997
|
+
'yourUsedTokens', CASE WHEN p_viewer IS NULL THEN NULL
|
|
13998
|
+
ELSE COALESCE(mu.used_tokens, 0) END,
|
|
13999
|
+
-- What the used figure is made of. Same window, same scope,
|
|
14000
|
+
-- same 'user' turns as the meter, so the four add up to it.
|
|
14001
|
+
'inputTokens', COALESCE(ls.i, 0),
|
|
14002
|
+
'outputTokens', COALESCE(ls.o, 0),
|
|
14003
|
+
'cacheReadTokens', COALESCE(ls.cr, 0),
|
|
14004
|
+
'cacheWriteTokens', COALESCE(ls.cw, 0),
|
|
14005
|
+
'yourInputTokens', CASE WHEN p_viewer IS NULL THEN NULL ELSE COALESCE(lu.i, 0) END,
|
|
14006
|
+
'yourOutputTokens', CASE WHEN p_viewer IS NULL THEN NULL ELSE COALESCE(lu.o, 0) END,
|
|
14007
|
+
'yourCacheReadTokens', CASE WHEN p_viewer IS NULL THEN NULL ELSE COALESCE(lu.cr, 0) END,
|
|
14008
|
+
'yourCacheWriteTokens', CASE WHEN p_viewer IS NULL THEN NULL ELSE COALESCE(lu.cw, 0) END,
|
|
14009
|
+
'windowStartUtc', to_char(w.window_start AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'),
|
|
14010
|
+
'resetsAtUtc', to_char(w.resets_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')))
|
|
14011
|
+
FROM grid_rows g
|
|
14012
|
+
CROSS JOIN windows w
|
|
14013
|
+
LEFT JOIN ${s}.provider_budget_rules r
|
|
14014
|
+
ON r.provider_name = g.name AND r.period = w.period
|
|
14015
|
+
AND COALESCE(r.model_qualified, '*') = g.scope
|
|
14016
|
+
LEFT JOIN ${s}.provider_meters m
|
|
14017
|
+
ON m.provider_name = g.name AND m.period = w.period
|
|
14018
|
+
AND m.scope = g.scope AND m.window_key_utc = w.window_key
|
|
14019
|
+
LEFT JOIN ${s}.provider_meters_user mu
|
|
14020
|
+
ON mu.provider_name = g.name AND mu.period = w.period
|
|
14021
|
+
AND mu.scope = g.scope AND mu.window_key_utc = w.window_key
|
|
14022
|
+
AND mu.user_id = p_viewer
|
|
14023
|
+
LEFT JOIN LATERAL (
|
|
14024
|
+
SELECT sum(l.tokens_input) i, sum(l.tokens_output) o,
|
|
14025
|
+
sum(l.tokens_cache_read) cr, sum(l.tokens_cache_write) cw
|
|
14026
|
+
FROM ${s}.provider_usage_ledger l
|
|
14027
|
+
WHERE l.provider_name = g.name AND l.charge_class = 'user'
|
|
14028
|
+
AND l.created_at >= GREATEST(w.window_start, g.named_at) AND l.created_at < w.resets_at
|
|
14029
|
+
AND (g.scope = '*' OR l.model_qualified = g.scope)) ls ON TRUE
|
|
14030
|
+
LEFT JOIN LATERAL (
|
|
14031
|
+
SELECT sum(l.tokens_input) i, sum(l.tokens_output) o,
|
|
14032
|
+
sum(l.tokens_cache_read) cr, sum(l.tokens_cache_write) cw
|
|
14033
|
+
FROM ${s}.provider_usage_ledger l
|
|
14034
|
+
WHERE l.provider_name = g.name AND l.charge_class = 'user'
|
|
14035
|
+
AND l.owner_user_id = p_viewer
|
|
14036
|
+
AND l.created_at >= GREATEST(w.window_start, g.named_at) AND l.created_at < w.resets_at
|
|
14037
|
+
AND (g.scope = '*' OR l.model_qualified = g.scope)) lu ON TRUE
|
|
14038
|
+
GROUP BY g.name, g.row_kind, g.scope, g.class, g.allowance_pct,
|
|
14039
|
+
g.hold_until_utc, g.hold_indefinite, g.model_rows,
|
|
14040
|
+
g.owned_by_me, g.manageable, g.owner_label
|
|
14041
|
+
ORDER BY (g.class = 'shared') DESC, g.name, (g.row_kind = 'model'), g.scope;
|
|
14042
|
+
$$ LANGUAGE sql STABLE;
|
|
14043
|
+
|
|
14044
|
+
DROP FUNCTION IF EXISTS ${s}.cms_provider_usage_daily(BIGINT, BOOLEAN, INTEGER, BIGINT, TEXT, TEXT, TEXT, TEXT);
|
|
14045
|
+
|
|
14046
|
+
CREATE FUNCTION ${s}.cms_provider_usage_daily(
|
|
14047
|
+
p_viewer BIGINT, p_is_admin BOOLEAN, p_days INTEGER,
|
|
14048
|
+
p_owner BIGINT, p_provider TEXT, p_model TEXT, p_session TEXT, p_class TEXT
|
|
14049
|
+
) RETURNS TABLE(
|
|
14050
|
+
day_utc DATE, tokens_total BIGINT, turns BIGINT,
|
|
14051
|
+
tokens_input BIGINT, tokens_output BIGINT, tokens_cache_read BIGINT, tokens_cache_write BIGINT
|
|
14052
|
+
) AS $$
|
|
14053
|
+
SELECT (l.created_at AT TIME ZONE 'UTC')::date, COALESCE(sum(l.tokens_total), 0), count(*)::BIGINT,
|
|
14054
|
+
COALESCE(sum(l.tokens_input), 0), COALESCE(sum(l.tokens_output), 0),
|
|
14055
|
+
COALESCE(sum(l.tokens_cache_read), 0), COALESCE(sum(l.tokens_cache_write), 0)
|
|
14056
|
+
FROM ${s}.provider_usage_ledger l
|
|
14057
|
+
WHERE l.created_at >= now() - (COALESCE(p_days, 7) || ' days')::interval
|
|
14058
|
+
AND (COALESCE(p_is_admin, FALSE)
|
|
14059
|
+
OR l.owner_user_id IS NOT DISTINCT FROM p_viewer
|
|
14060
|
+
OR (p_provider IS NOT NULL AND p_owner IS NULL AND p_session IS NULL
|
|
14061
|
+
AND EXISTS (
|
|
14062
|
+
SELECT 1 FROM ${s}.provider_instances pi
|
|
14063
|
+
WHERE pi.name = p_provider AND pi.class = 'shared')))
|
|
14064
|
+
AND (p_owner IS NULL OR l.owner_user_id = p_owner)
|
|
14065
|
+
AND (p_provider IS NULL OR l.provider_name = p_provider)
|
|
14066
|
+
AND (p_provider IS NULL OR l.created_at >= COALESCE(
|
|
14067
|
+
(SELECT pi.created_at FROM ${s}.provider_instances pi WHERE pi.name = p_provider),
|
|
14068
|
+
'-infinity'::timestamptz))
|
|
14069
|
+
AND (p_model IS NULL OR l.model_qualified = p_model)
|
|
14070
|
+
AND (p_session IS NULL OR l.session_id = p_session)
|
|
14071
|
+
AND (p_class IS NULL OR l.charge_class = p_class)
|
|
14072
|
+
GROUP BY 1 ORDER BY 1;
|
|
14073
|
+
$$ LANGUAGE sql STABLE;
|
|
14074
|
+
`;
|
|
14075
|
+
}
|
|
14076
|
+
/**
|
|
14077
|
+
* 0070: a turn's total is input + output. Cache reads and writes are parts
|
|
14078
|
+
* of the input, not additions to it.
|
|
14079
|
+
*
|
|
14080
|
+
* `buildUsageSummaryUpsert` (session-proxy.ts) stores the INCLUSIVE prompt
|
|
14081
|
+
* count: `tokens_input` already contains what was served from the cache and
|
|
14082
|
+
* what was written to it. The ledger and the meters proved it — in every row
|
|
14083
|
+
* on the local database and on chk, across GPT, Claude, Grok and MAI,
|
|
14084
|
+
* `tokens_cache_read + tokens_cache_write <= tokens_input`, and when a turn
|
|
14085
|
+
* wrote to the cache the two sides were equal. Yet `cms_provider_settle_turn`
|
|
14086
|
+
* summed all four into `tokens_total`, so every cached prompt was charged
|
|
14087
|
+
* twice: chk's last 30 days read 3.62B tokens for 1.97B consumed. That
|
|
14088
|
+
* total is what the meters hold and what every limit is compared against,
|
|
14089
|
+
* so quotas were biting at roughly half their stated size.
|
|
14090
|
+
*
|
|
14091
|
+
* Three things, as separate steps: the function, the ledger backfill, and
|
|
14092
|
+
* the meters rebuilt from the corrected ledger for their live windows (the
|
|
14093
|
+
* meters are a cache of the ledger; the reconciliation that showed them
|
|
14094
|
+
* exact is the same query, so nothing here invents a number). After this,
|
|
14095
|
+
* every "used" figure drops by the cached amount — limits that were tuned
|
|
14096
|
+
* against the inflated figures are looser in practice from here on.
|
|
14097
|
+
*/
|
|
14098
|
+
function migration_0070_token_total_is_input_plus_output(schema) {
|
|
14099
|
+
const s = schema;
|
|
14100
|
+
return [
|
|
14101
|
+
`
|
|
14102
|
+
CREATE OR REPLACE FUNCTION ${s}.cms_provider_settle_turn(
|
|
14103
|
+
p_session_id TEXT, p_turn_index INTEGER, p_provider TEXT, p_model TEXT,
|
|
14104
|
+
p_owner BIGINT, p_charge_class TEXT, p_agent_id TEXT,
|
|
14105
|
+
p_in BIGINT, p_out BIGINT, p_cache_read BIGINT, p_cache_write BIGINT
|
|
14106
|
+
) RETURNS BOOLEAN AS $$
|
|
14107
|
+
DECLARE
|
|
14108
|
+
-- input + output. The cache figures are NOT added: as the Copilot SDK
|
|
14109
|
+
-- reports usage, cache_read and cache_write are parts OF the input
|
|
14110
|
+
-- (cache_read + cache_write <= input in every row ever recorded, and
|
|
14111
|
+
-- equal to it when writes occur), so adding them again charged a
|
|
14112
|
+
-- cached prompt twice — chk was over-counting by 84% (0070).
|
|
14113
|
+
v_total BIGINT := COALESCE(p_in,0) + COALESCE(p_out,0);
|
|
14114
|
+
v_class TEXT := COALESCE(NULLIF(BTRIM(COALESCE(p_charge_class,'')), ''), 'user');
|
|
14115
|
+
v_scope TEXT := COALESCE(NULLIF(BTRIM(COALESCE(p_model,'')), ''), '*');
|
|
14116
|
+
-- The lifetime offset. Stable for the whole turn: only a restart moves
|
|
14117
|
+
-- it, and a restart deletes the orchestration mid-turn anyway.
|
|
14118
|
+
v_index INTEGER := COALESCE(p_turn_index, 0) + COALESCE(
|
|
14119
|
+
(SELECT ss.provider_ledger_base FROM ${s}.sessions ss
|
|
14120
|
+
WHERE ss.session_id = p_session_id), 0);
|
|
14121
|
+
v_first INTEGER;
|
|
14122
|
+
BEGIN
|
|
14123
|
+
IF p_provider IS NULL THEN v_class := 'unattributed'; END IF;
|
|
14124
|
+
|
|
14125
|
+
INSERT INTO ${s}.provider_usage_ledger
|
|
14126
|
+
(session_id, turn_index, provider_name, model_qualified, owner_user_id,
|
|
14127
|
+
charge_class, tokens_input, tokens_output, tokens_cache_read,
|
|
14128
|
+
tokens_cache_write, tokens_total, agent_id)
|
|
14129
|
+
VALUES (p_session_id, v_index, p_provider, p_model, p_owner,
|
|
14130
|
+
v_class, COALESCE(p_in,0), COALESCE(p_out,0), COALESCE(p_cache_read,0),
|
|
14131
|
+
COALESCE(p_cache_write,0), v_total, p_agent_id)
|
|
14132
|
+
ON CONFLICT (session_id, turn_index) DO NOTHING;
|
|
14133
|
+
GET DIAGNOSTICS v_first = ROW_COUNT;
|
|
14134
|
+
IF v_first = 0 THEN RETURN FALSE; END IF;
|
|
14135
|
+
|
|
14136
|
+
IF v_class <> 'user' OR p_provider IS NULL OR v_total <= 0 THEN
|
|
14137
|
+
RETURN TRUE;
|
|
14138
|
+
END IF;
|
|
14139
|
+
|
|
14140
|
+
PERFORM 1 FROM ${s}.provider_instances pi
|
|
14141
|
+
WHERE pi.name = p_provider FOR KEY SHARE;
|
|
14142
|
+
IF NOT FOUND THEN RETURN TRUE; END IF;
|
|
14143
|
+
|
|
14144
|
+
INSERT INTO ${s}.provider_meters
|
|
14145
|
+
(provider_name, period, scope, window_key_utc, used_tokens,
|
|
14146
|
+
window_start_utc, resets_at_utc)
|
|
14147
|
+
SELECT p_provider, per.period, sc.scope, wb.window_key, v_total,
|
|
14148
|
+
wb.window_start, wb.resets_at
|
|
14149
|
+
FROM (VALUES ('day'),('week'),('month')) AS per(period)
|
|
14150
|
+
CROSS JOIN LATERAL ${s}.cms_provider_window_bounds(per.period, now()) wb
|
|
14151
|
+
CROSS JOIN (SELECT DISTINCT v.s FROM (VALUES ('*'), (v_scope)) AS v(s)) AS sc(scope)
|
|
14152
|
+
ORDER BY per.period, sc.scope
|
|
14153
|
+
ON CONFLICT (provider_name, period, scope, window_key_utc) DO UPDATE
|
|
14154
|
+
SET used_tokens = ${s}.provider_meters.used_tokens + EXCLUDED.used_tokens,
|
|
14155
|
+
updated_at = now();
|
|
14156
|
+
|
|
14157
|
+
IF p_owner IS NOT NULL THEN
|
|
14158
|
+
INSERT INTO ${s}.provider_meters_user
|
|
14159
|
+
(provider_name, period, scope, window_key_utc, user_id, used_tokens,
|
|
14160
|
+
window_start_utc, resets_at_utc)
|
|
14161
|
+
SELECT p_provider, per.period, sc.scope, wb.window_key, p_owner, v_total,
|
|
14162
|
+
wb.window_start, wb.resets_at
|
|
14163
|
+
FROM (VALUES ('day'),('week'),('month')) AS per(period)
|
|
14164
|
+
CROSS JOIN LATERAL ${s}.cms_provider_window_bounds(per.period, now()) wb
|
|
14165
|
+
CROSS JOIN (SELECT DISTINCT v.s FROM (VALUES ('*'), (v_scope)) AS v(s)) AS sc(scope)
|
|
14166
|
+
ORDER BY per.period, sc.scope
|
|
14167
|
+
ON CONFLICT (provider_name, period, scope, window_key_utc, user_id) DO UPDATE
|
|
14168
|
+
SET used_tokens = ${s}.provider_meters_user.used_tokens + EXCLUDED.used_tokens,
|
|
14169
|
+
updated_at = now();
|
|
14170
|
+
END IF;
|
|
14171
|
+
|
|
14172
|
+
RETURN TRUE;
|
|
14173
|
+
END;
|
|
14174
|
+
$$ LANGUAGE plpgsql VOLATILE;
|
|
14175
|
+
`,
|
|
14176
|
+
`
|
|
14177
|
+
SET lock_timeout = '5s';
|
|
14178
|
+
UPDATE ${s}.provider_usage_ledger
|
|
14179
|
+
SET tokens_total = tokens_input + tokens_output
|
|
14180
|
+
WHERE tokens_total IS DISTINCT FROM tokens_input + tokens_output;
|
|
14181
|
+
`,
|
|
14182
|
+
`
|
|
14183
|
+
UPDATE ${s}.provider_meters m
|
|
14184
|
+
SET used_tokens = COALESCE((
|
|
14185
|
+
SELECT sum(l.tokens_total)
|
|
14186
|
+
FROM ${s}.provider_usage_ledger l
|
|
14187
|
+
WHERE l.provider_name = m.provider_name
|
|
14188
|
+
AND l.charge_class = 'user'
|
|
14189
|
+
AND l.created_at >= GREATEST(m.window_start_utc,
|
|
14190
|
+
COALESCE((SELECT pi.created_at FROM ${s}.provider_instances pi WHERE pi.name = m.provider_name), '-infinity'::timestamptz))
|
|
14191
|
+
AND l.created_at < m.resets_at_utc
|
|
14192
|
+
AND (m.scope = '*' OR l.model_qualified = m.scope)), 0),
|
|
14193
|
+
updated_at = now();
|
|
14194
|
+
`,
|
|
14195
|
+
`
|
|
14196
|
+
UPDATE ${s}.provider_meters_user m
|
|
14197
|
+
SET used_tokens = COALESCE((
|
|
14198
|
+
SELECT sum(l.tokens_total)
|
|
14199
|
+
FROM ${s}.provider_usage_ledger l
|
|
14200
|
+
WHERE l.provider_name = m.provider_name
|
|
14201
|
+
AND l.charge_class = 'user'
|
|
14202
|
+
AND l.owner_user_id = m.user_id
|
|
14203
|
+
AND l.created_at >= GREATEST(m.window_start_utc,
|
|
14204
|
+
COALESCE((SELECT pi.created_at FROM ${s}.provider_instances pi WHERE pi.name = m.provider_name), '-infinity'::timestamptz))
|
|
14205
|
+
AND l.created_at < m.resets_at_utc
|
|
14206
|
+
AND (m.scope = '*' OR l.model_qualified = m.scope)), 0),
|
|
14207
|
+
updated_at = now();
|
|
14208
|
+
`,
|
|
14209
|
+
];
|
|
14210
|
+
}
|
|
13771
14211
|
//# sourceMappingURL=cms-migrations.js.map
|