@acarmisc/backstage-plugin-litellm-backend 0.11.1 → 0.13.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/config.d.ts +28 -0
- package/dist/client.js +2 -0
- package/dist/index.cjs.js +128 -9
- package/dist/index.cjs.js.map +3 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -1
- package/dist/openapi.js +9 -0
- package/dist/router.js +59 -9
- package/dist/teamBudgetVisibility.d.ts +48 -0
- package/dist/teamBudgetVisibility.js +76 -0
- package/dist/types.cjs.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/package.json +1 -1
package/config.d.ts
CHANGED
|
@@ -248,6 +248,34 @@ export interface Config {
|
|
|
248
248
|
};
|
|
249
249
|
};
|
|
250
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Display controls for team budget amounts in the frontend.
|
|
253
|
+
* Each flag independently hides real dollar budgets while the backend
|
|
254
|
+
* still exposes the consumption level (percent of cap + ok/near/over
|
|
255
|
+
* status + reset window). Enforcement is server-side: the backend
|
|
256
|
+
* redacts max_budget/spend from the corresponding endpoints, so the
|
|
257
|
+
* dollars are not merely hidden in CSS.
|
|
258
|
+
*/
|
|
259
|
+
display?: {
|
|
260
|
+
/**
|
|
261
|
+
* Hide dollar budgets from regular team members (GET /teams, Teams
|
|
262
|
+
* cards, TEAM section of the budget widget). Team managers still see
|
|
263
|
+
* dollars unless hideTeamBudgetForManagers is also set.
|
|
264
|
+
* @default false
|
|
265
|
+
* @visibility frontend
|
|
266
|
+
*/
|
|
267
|
+
hideTeamBudgetForMembers?: boolean;
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Hide dollar budgets even from team managers (GET /teams/managed,
|
|
271
|
+
* team write responses, ManageTeamDialog budget field which becomes
|
|
272
|
+
* write-only). Useful when budgets are finance-sensitive.
|
|
273
|
+
* @default false
|
|
274
|
+
* @visibility frontend
|
|
275
|
+
*/
|
|
276
|
+
hideTeamBudgetForManagers?: boolean;
|
|
277
|
+
};
|
|
278
|
+
|
|
251
279
|
bridge?: {
|
|
252
280
|
/**
|
|
253
281
|
* When true, mount the /bridge/keys, /bridge/keys (POST), /bridge/models
|
package/dist/client.js
CHANGED
|
@@ -98,6 +98,7 @@ class LiteLLMClient {
|
|
|
98
98
|
teams: teamIds,
|
|
99
99
|
models: inner.models ?? raw?.models,
|
|
100
100
|
max_budget: inner.max_budget ?? raw?.max_budget,
|
|
101
|
+
budget_duration: inner.budget_duration ?? raw?.budget_duration,
|
|
101
102
|
spend: inner.spend ?? raw?.spend,
|
|
102
103
|
current_spend: inner.current_spend ?? raw?.current_spend,
|
|
103
104
|
soft_limit: inner.soft_limit ?? raw?.soft_limit,
|
|
@@ -162,6 +163,7 @@ class LiteLLMClient {
|
|
|
162
163
|
expires_at: k.expires ?? undefined,
|
|
163
164
|
spend: k.spend ?? 0,
|
|
164
165
|
max_budget: k.max_budget ?? undefined,
|
|
166
|
+
budget_duration: k.budget_duration ?? undefined,
|
|
165
167
|
tpm_limit: k.tpm_limit ?? undefined,
|
|
166
168
|
rpm_limit: k.rpm_limit ?? undefined,
|
|
167
169
|
models: k.models ?? [],
|
package/dist/index.cjs.js
CHANGED
|
@@ -8002,10 +8002,14 @@ __export(index_exports, {
|
|
|
8002
8002
|
readProvisioningDefaults: () => readProvisioningDefaults,
|
|
8003
8003
|
readRoleConfigs: () => readRoleConfigs,
|
|
8004
8004
|
readTeamAdminConfig: () => readTeamAdminConfig,
|
|
8005
|
+
readTeamBudgetVisibility: () => readTeamBudgetVisibility,
|
|
8006
|
+
redactTeamBudget: () => redactTeamBudget,
|
|
8007
|
+
redactTeamUsage: () => redactTeamUsage,
|
|
8005
8008
|
resolveBridgeUserId: () => resolveBridgeUserId,
|
|
8006
8009
|
resolveUserId: () => resolveUserId,
|
|
8007
8010
|
resolveUserProfile: () => resolveUserProfile,
|
|
8008
8011
|
resolveUserRole: () => resolveUserRole,
|
|
8012
|
+
teamBudgetStatusFor: () => teamBudgetStatusFor,
|
|
8009
8013
|
toLiteLLMUserId: () => toLiteLLMUserId,
|
|
8010
8014
|
validateTeamPatchInput: () => validateTeamPatchInput,
|
|
8011
8015
|
validateTeamWriteInput: () => validateTeamWriteInput
|
|
@@ -8099,6 +8103,7 @@ var LiteLLMClient = class {
|
|
|
8099
8103
|
teams: teamIds,
|
|
8100
8104
|
models: inner.models ?? raw?.models,
|
|
8101
8105
|
max_budget: inner.max_budget ?? raw?.max_budget,
|
|
8106
|
+
budget_duration: inner.budget_duration ?? raw?.budget_duration,
|
|
8102
8107
|
spend: inner.spend ?? raw?.spend,
|
|
8103
8108
|
current_spend: inner.current_spend ?? raw?.current_spend,
|
|
8104
8109
|
soft_limit: inner.soft_limit ?? raw?.soft_limit,
|
|
@@ -8161,6 +8166,7 @@ var LiteLLMClient = class {
|
|
|
8161
8166
|
expires_at: k.expires ?? void 0,
|
|
8162
8167
|
spend: k.spend ?? 0,
|
|
8163
8168
|
max_budget: k.max_budget ?? void 0,
|
|
8169
|
+
budget_duration: k.budget_duration ?? void 0,
|
|
8164
8170
|
tpm_limit: k.tpm_limit ?? void 0,
|
|
8165
8171
|
rpm_limit: k.rpm_limit ?? void 0,
|
|
8166
8172
|
models: k.models ?? [],
|
|
@@ -8627,6 +8633,10 @@ var openApiSpec = {
|
|
|
8627
8633
|
keyGeneration: { type: "object", properties: {
|
|
8628
8634
|
allowUnlimitedBudget: { type: "boolean" },
|
|
8629
8635
|
teamRequired: { type: "boolean" }
|
|
8636
|
+
} },
|
|
8637
|
+
display: { type: "object", properties: {
|
|
8638
|
+
hideTeamBudgetForMembers: { type: "boolean" },
|
|
8639
|
+
hideTeamBudgetForManagers: { type: "boolean" }
|
|
8630
8640
|
} }
|
|
8631
8641
|
} } } }
|
|
8632
8642
|
}
|
|
@@ -8731,6 +8741,7 @@ var openApiSpec = {
|
|
|
8731
8741
|
get: {
|
|
8732
8742
|
tags: ["Teams"],
|
|
8733
8743
|
summary: "List teams the current user belongs to",
|
|
8744
|
+
description: "When litellm.display.hideTeamBudgetForMembers is set, max_budget/spend are stripped and budget_pct/budget_status/budget_hidden carry the consumption level instead.",
|
|
8734
8745
|
responses: { "200": { description: "Array of teams" } }
|
|
8735
8746
|
}
|
|
8736
8747
|
},
|
|
@@ -8738,6 +8749,7 @@ var openApiSpec = {
|
|
|
8738
8749
|
get: {
|
|
8739
8750
|
tags: ["Teams"],
|
|
8740
8751
|
summary: "Usage metrics for a team",
|
|
8752
|
+
description: "Spend fields are zeroed when the caller is subject to team budget hiding (member or manager flag, whichever applies to them).",
|
|
8741
8753
|
parameters: [
|
|
8742
8754
|
{ name: "teamId", in: "path", required: true, schema: { type: "string" } },
|
|
8743
8755
|
{ name: "start_date", in: "query", required: true, schema: { type: "string" } },
|
|
@@ -10825,6 +10837,65 @@ function validateTeamPatchInput(input, cfg) {
|
|
|
10825
10837
|
return { ok: true, value: result };
|
|
10826
10838
|
}
|
|
10827
10839
|
|
|
10840
|
+
// src/teamBudgetVisibility.ts
|
|
10841
|
+
function teamBudgetStatusFor(spend, budget) {
|
|
10842
|
+
if (budget > 0 && spend >= budget) return "over";
|
|
10843
|
+
if (budget > 0 && spend >= budget * 0.8) return "near";
|
|
10844
|
+
return "ok";
|
|
10845
|
+
}
|
|
10846
|
+
function readTeamBudgetVisibility(config) {
|
|
10847
|
+
return {
|
|
10848
|
+
hideTeamBudgetForMembers: config.getOptionalBoolean(
|
|
10849
|
+
"litellm.display.hideTeamBudgetForMembers"
|
|
10850
|
+
) ?? false,
|
|
10851
|
+
hideTeamBudgetForManagers: config.getOptionalBoolean(
|
|
10852
|
+
"litellm.display.hideTeamBudgetForManagers"
|
|
10853
|
+
) ?? false
|
|
10854
|
+
};
|
|
10855
|
+
}
|
|
10856
|
+
function redactTeamBudget(team) {
|
|
10857
|
+
const budget = team.max_budget ?? 0;
|
|
10858
|
+
if (budget <= 0) return team;
|
|
10859
|
+
const spend = team.spend ?? 0;
|
|
10860
|
+
const pct = Math.min(100, Math.max(0, spend / budget * 100));
|
|
10861
|
+
const { max_budget: _dropped, ...rest } = team;
|
|
10862
|
+
return {
|
|
10863
|
+
...rest,
|
|
10864
|
+
spend: 0,
|
|
10865
|
+
budget_pct: pct,
|
|
10866
|
+
budget_status: teamBudgetStatusFor(spend, budget),
|
|
10867
|
+
budget_hidden: true
|
|
10868
|
+
};
|
|
10869
|
+
}
|
|
10870
|
+
function redactTeamUsage(usage) {
|
|
10871
|
+
const zeroSpend = (p) => ({
|
|
10872
|
+
...p,
|
|
10873
|
+
spend: 0
|
|
10874
|
+
});
|
|
10875
|
+
const zeroTotalSpend = (p) => ({
|
|
10876
|
+
...p,
|
|
10877
|
+
total_spend: 0
|
|
10878
|
+
});
|
|
10879
|
+
return {
|
|
10880
|
+
...usage,
|
|
10881
|
+
total_spend: 0,
|
|
10882
|
+
usage_by_model: Object.fromEntries(
|
|
10883
|
+
Object.entries(usage.usage_by_model ?? {}).map(([k, v]) => [
|
|
10884
|
+
k,
|
|
10885
|
+
zeroTotalSpend(v)
|
|
10886
|
+
])
|
|
10887
|
+
),
|
|
10888
|
+
usage_by_key: Object.fromEntries(
|
|
10889
|
+
Object.entries(usage.usage_by_key ?? {}).map(([k, v]) => [
|
|
10890
|
+
k,
|
|
10891
|
+
zeroTotalSpend(v)
|
|
10892
|
+
])
|
|
10893
|
+
),
|
|
10894
|
+
daily_usage: (usage.daily_usage ?? []).map(zeroSpend),
|
|
10895
|
+
daily_by_model: (usage.daily_by_model ?? []).map(zeroSpend)
|
|
10896
|
+
};
|
|
10897
|
+
}
|
|
10898
|
+
|
|
10828
10899
|
// src/router.ts
|
|
10829
10900
|
var teamCreateInFlight = /* @__PURE__ */ new Map();
|
|
10830
10901
|
var TEAM_FETCH_RETRY_DELAYS_MS = [100, 300, 900];
|
|
@@ -10861,6 +10932,7 @@ async function createRouter(options) {
|
|
|
10861
10932
|
const teamMgmtEnabled = isTeamManagementEnabled(config);
|
|
10862
10933
|
const objectPermsEnabled = isObjectPermissionsEnabled(config);
|
|
10863
10934
|
const teamAdminCfg = readTeamAdminConfig(config);
|
|
10935
|
+
const teamBudgetVisibility = readTeamBudgetVisibility(config);
|
|
10864
10936
|
const catalogClient = options.catalogClient ?? new import_catalog_client.CatalogClient({ discoveryApi: discovery });
|
|
10865
10937
|
if (provisioningEnabled) {
|
|
10866
10938
|
logger.info(
|
|
@@ -10872,6 +10944,11 @@ async function createRouter(options) {
|
|
|
10872
10944
|
"litellm.teamAdmin is configured but team management is disabled \u2014 set permission.enabled: true and litellm.teamAdmin.group to enable it."
|
|
10873
10945
|
);
|
|
10874
10946
|
}
|
|
10947
|
+
if (teamBudgetVisibility.hideTeamBudgetForMembers || teamBudgetVisibility.hideTeamBudgetForManagers) {
|
|
10948
|
+
logger.info(
|
|
10949
|
+
`LiteLLM team budget hiding enabled \u2014 members: ${teamBudgetVisibility.hideTeamBudgetForMembers}, managers: ${teamBudgetVisibility.hideTeamBudgetForManagers}`
|
|
10950
|
+
);
|
|
10951
|
+
}
|
|
10875
10952
|
const router = (0, import_express.Router)();
|
|
10876
10953
|
router.use(import_express.default.json());
|
|
10877
10954
|
router.get("/health", (_req, res) => {
|
|
@@ -10886,6 +10963,10 @@ async function createRouter(options) {
|
|
|
10886
10963
|
maxBudgetCeiling: teamAdminCfg.maxBudgetCeiling,
|
|
10887
10964
|
allowUnlimitedBudget: teamAdminCfg.allowUnlimitedBudget,
|
|
10888
10965
|
objectPermissionsEnabled: objectPermsEnabled
|
|
10966
|
+
},
|
|
10967
|
+
display: {
|
|
10968
|
+
hideTeamBudgetForMembers: teamBudgetVisibility.hideTeamBudgetForMembers,
|
|
10969
|
+
hideTeamBudgetForManagers: teamBudgetVisibility.hideTeamBudgetForManagers
|
|
10889
10970
|
}
|
|
10890
10971
|
});
|
|
10891
10972
|
});
|
|
@@ -11285,7 +11366,10 @@ async function createRouter(options) {
|
|
|
11285
11366
|
})
|
|
11286
11367
|
)
|
|
11287
11368
|
);
|
|
11288
|
-
|
|
11369
|
+
const list = teams.filter(Boolean);
|
|
11370
|
+
res.json(
|
|
11371
|
+
teamBudgetVisibility.hideTeamBudgetForMembers ? list.map(redactTeamBudget) : list
|
|
11372
|
+
);
|
|
11289
11373
|
} catch (error) {
|
|
11290
11374
|
if (error instanceof ProvisioningError) {
|
|
11291
11375
|
res.status(error.status).json(error.body);
|
|
@@ -11384,7 +11468,9 @@ async function createRouter(options) {
|
|
|
11384
11468
|
teamCreateInFlight.set(key, createPromise);
|
|
11385
11469
|
try {
|
|
11386
11470
|
const result = await createPromise;
|
|
11387
|
-
res.json(
|
|
11471
|
+
res.json(
|
|
11472
|
+
teamBudgetVisibility.hideTeamBudgetForManagers ? redactTeamBudget(result) : result
|
|
11473
|
+
);
|
|
11388
11474
|
} catch (err) {
|
|
11389
11475
|
sendTeamError(err, res);
|
|
11390
11476
|
} finally {
|
|
@@ -11428,7 +11514,9 @@ async function createRouter(options) {
|
|
|
11428
11514
|
teamId,
|
|
11429
11515
|
owningGroup
|
|
11430
11516
|
});
|
|
11431
|
-
res.json(
|
|
11517
|
+
res.json(
|
|
11518
|
+
teamBudgetVisibility.hideTeamBudgetForManagers ? redactTeamBudget(r) : r
|
|
11519
|
+
);
|
|
11432
11520
|
} catch (err) {
|
|
11433
11521
|
sendTeamError(err, res);
|
|
11434
11522
|
}
|
|
@@ -11486,7 +11574,9 @@ async function createRouter(options) {
|
|
|
11486
11574
|
const owned = all.filter(
|
|
11487
11575
|
(t) => typeof t.metadata?.owning_group === "string" && t.metadata.owning_group === teamAdminCfg.group
|
|
11488
11576
|
);
|
|
11489
|
-
res.json(
|
|
11577
|
+
res.json(
|
|
11578
|
+
teamBudgetVisibility.hideTeamBudgetForManagers ? owned.map(redactTeamBudget) : owned
|
|
11579
|
+
);
|
|
11490
11580
|
} catch (err) {
|
|
11491
11581
|
sendTeamError(err, res);
|
|
11492
11582
|
}
|
|
@@ -11628,7 +11718,9 @@ async function createRouter(options) {
|
|
|
11628
11718
|
owningGroup
|
|
11629
11719
|
});
|
|
11630
11720
|
const updated = await withTeamFetchRetry(() => client.getTeamInfo(teamId));
|
|
11631
|
-
res.json(
|
|
11721
|
+
res.json(
|
|
11722
|
+
teamBudgetVisibility.hideTeamBudgetForManagers ? redactTeamBudget(updated) : updated
|
|
11723
|
+
);
|
|
11632
11724
|
} catch (err) {
|
|
11633
11725
|
sendTeamError(err, res);
|
|
11634
11726
|
}
|
|
@@ -11659,7 +11751,9 @@ async function createRouter(options) {
|
|
|
11659
11751
|
owningGroup
|
|
11660
11752
|
});
|
|
11661
11753
|
const updated = await withTeamFetchRetry(() => client.getTeamInfo(teamId));
|
|
11662
|
-
res.json(
|
|
11754
|
+
res.json(
|
|
11755
|
+
teamBudgetVisibility.hideTeamBudgetForManagers ? redactTeamBudget(updated) : updated
|
|
11756
|
+
);
|
|
11663
11757
|
} catch (err) {
|
|
11664
11758
|
sendTeamError(err, res);
|
|
11665
11759
|
}
|
|
@@ -11738,7 +11832,9 @@ async function createRouter(options) {
|
|
|
11738
11832
|
owningGroup,
|
|
11739
11833
|
vector_stores: requested
|
|
11740
11834
|
});
|
|
11741
|
-
res.json(
|
|
11835
|
+
res.json(
|
|
11836
|
+
teamBudgetVisibility.hideTeamBudgetForManagers ? redactTeamBudget(updated) : updated
|
|
11837
|
+
);
|
|
11742
11838
|
} catch (err) {
|
|
11743
11839
|
sendTeamError(err, res);
|
|
11744
11840
|
}
|
|
@@ -11819,7 +11915,9 @@ async function createRouter(options) {
|
|
|
11819
11915
|
before,
|
|
11820
11916
|
after: requested
|
|
11821
11917
|
});
|
|
11822
|
-
res.json(
|
|
11918
|
+
res.json(
|
|
11919
|
+
teamBudgetVisibility.hideTeamBudgetForManagers ? redactTeamBudget(updated) : updated
|
|
11920
|
+
);
|
|
11823
11921
|
} catch (err) {
|
|
11824
11922
|
sendTeamError(err, res);
|
|
11825
11923
|
}
|
|
@@ -11838,7 +11936,24 @@ async function createRouter(options) {
|
|
|
11838
11936
|
start_date,
|
|
11839
11937
|
end_date
|
|
11840
11938
|
);
|
|
11841
|
-
|
|
11939
|
+
let hide = teamBudgetVisibility.hideTeamBudgetForMembers;
|
|
11940
|
+
if (teamAdminCfg.group) {
|
|
11941
|
+
try {
|
|
11942
|
+
const tokenEntityRef = await resolveUserId(req, auth);
|
|
11943
|
+
const isManager = !!tokenEntityRef && await isUserMemberOfGroup(
|
|
11944
|
+
tokenEntityRef,
|
|
11945
|
+
teamAdminCfg.group,
|
|
11946
|
+
catalogClient,
|
|
11947
|
+
auth,
|
|
11948
|
+
logger
|
|
11949
|
+
);
|
|
11950
|
+
hide = isManager ? teamBudgetVisibility.hideTeamBudgetForManagers : teamBudgetVisibility.hideTeamBudgetForMembers;
|
|
11951
|
+
} catch (err) {
|
|
11952
|
+
logger.warn(`Team-manager check failed, hiding team spend: ${err.message}`);
|
|
11953
|
+
hide = teamBudgetVisibility.hideTeamBudgetForMembers;
|
|
11954
|
+
}
|
|
11955
|
+
}
|
|
11956
|
+
res.json(hide ? redactTeamUsage(usage) : usage);
|
|
11842
11957
|
} catch (error) {
|
|
11843
11958
|
logger.error("Failed to fetch team usage", error);
|
|
11844
11959
|
res.status(500).json({ error: error.message });
|
|
@@ -12040,10 +12155,14 @@ var litellmPlugin = (0, import_backend_plugin_api.createBackendPlugin)({
|
|
|
12040
12155
|
readProvisioningDefaults,
|
|
12041
12156
|
readRoleConfigs,
|
|
12042
12157
|
readTeamAdminConfig,
|
|
12158
|
+
readTeamBudgetVisibility,
|
|
12159
|
+
redactTeamBudget,
|
|
12160
|
+
redactTeamUsage,
|
|
12043
12161
|
resolveBridgeUserId,
|
|
12044
12162
|
resolveUserId,
|
|
12045
12163
|
resolveUserProfile,
|
|
12046
12164
|
resolveUserRole,
|
|
12165
|
+
teamBudgetStatusFor,
|
|
12047
12166
|
toLiteLLMUserId,
|
|
12048
12167
|
validateTeamPatchInput,
|
|
12049
12168
|
validateTeamWriteInput
|