@acarmisc/backstage-plugin-litellm-backend 0.27.0 → 0.29.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 +20 -0
- package/dist/index.cjs.js +27 -16
- package/dist/index.cjs.js.map +2 -2
- package/dist/openapi.js +1 -0
- package/dist/router.js +5 -0
- package/dist/routes/config.js +1 -0
- package/dist/routes/context.d.ts +2 -0
- package/dist/routes/teamUsage.js +21 -11
- package/dist/teamMemberUsage.d.ts +2 -0
- package/dist/teamMemberUsage.js +1 -0
- package/package.json +1 -1
package/config.d.ts
CHANGED
|
@@ -450,6 +450,17 @@ export interface Config {
|
|
|
450
450
|
* @default false
|
|
451
451
|
*/
|
|
452
452
|
hideTeamBudgetForManagers?: boolean;
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Key-alias prefixes that identify system / service keys (e.g.
|
|
456
|
+
* ["svc-", "system-"]). The Keys table hides keys whose alias starts
|
|
457
|
+
* with one of these prefixes behind a "Hide service keys" toggle that
|
|
458
|
+
* is on by default; users can switch it off to see every key.
|
|
459
|
+
* Matching is case-insensitive. Display-only: the keys are still
|
|
460
|
+
* returned by the API and nothing is enforced server-side.
|
|
461
|
+
* @default []
|
|
462
|
+
*/
|
|
463
|
+
serviceKeyPrefixes?: string[];
|
|
453
464
|
};
|
|
454
465
|
|
|
455
466
|
/**
|
|
@@ -463,6 +474,8 @@ export interface Config {
|
|
|
463
474
|
* reaches:
|
|
464
475
|
* - managers of the team (members of its metadata.owning_group, with
|
|
465
476
|
* team management enabled);
|
|
477
|
+
* - team leads (members of the catalog group `<team_alias><leadGroupSuffix>`,
|
|
478
|
+
* when leadGroupSuffix is set);
|
|
466
479
|
* - members of the team holding one of `viewerRoles` in it;
|
|
467
480
|
* - members of the team granted `litellm.team.usage.read` (only
|
|
468
481
|
* consulted when permission.enabled is true).
|
|
@@ -480,6 +493,13 @@ export interface Config {
|
|
|
480
493
|
* @default []
|
|
481
494
|
*/
|
|
482
495
|
viewerRoles?: string[];
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Suffix of a catalog group named `<team_alias><suffix>` whose members
|
|
499
|
+
* are team leads (e.g. "-leads" -> group:default/abstrid-leads). For
|
|
500
|
+
* teams synced without metadata.owning_group. Unset: no lead access.
|
|
501
|
+
*/
|
|
502
|
+
leadGroupSuffix?: string;
|
|
483
503
|
};
|
|
484
504
|
};
|
|
485
505
|
|
package/dist/index.cjs.js
CHANGED
|
@@ -9449,7 +9449,8 @@ function readMemberBreakdownConfig(config) {
|
|
|
9449
9449
|
return {
|
|
9450
9450
|
enabled: config.getOptionalBoolean("litellm.teamUsage.memberBreakdown.enabled") ?? false,
|
|
9451
9451
|
viewerRoles: config.getOptionalStringArray("litellm.teamUsage.memberBreakdown.viewerRoles") ?? [],
|
|
9452
|
-
permissionEnabled: config.getOptionalBoolean("permission.enabled") ?? false
|
|
9452
|
+
permissionEnabled: config.getOptionalBoolean("permission.enabled") ?? false,
|
|
9453
|
+
leadGroupSuffix: config.getOptionalString("litellm.teamUsage.memberBreakdown.leadGroupSuffix") || void 0
|
|
9453
9454
|
};
|
|
9454
9455
|
}
|
|
9455
9456
|
function memberEmails(team, keys) {
|
|
@@ -9785,7 +9786,8 @@ var openApiSpec = {
|
|
|
9785
9786
|
} },
|
|
9786
9787
|
display: { type: "object", properties: {
|
|
9787
9788
|
hideTeamBudgetForMembers: { type: "boolean" },
|
|
9788
|
-
hideTeamBudgetForManagers: { type: "boolean" }
|
|
9789
|
+
hideTeamBudgetForManagers: { type: "boolean" },
|
|
9790
|
+
serviceKeyPrefixes: { type: "array", items: { type: "string" } }
|
|
9789
9791
|
} }
|
|
9790
9792
|
} } } }
|
|
9791
9793
|
}
|
|
@@ -10053,7 +10055,8 @@ function registerConfigRoutes(router, ctx) {
|
|
|
10053
10055
|
},
|
|
10054
10056
|
display: {
|
|
10055
10057
|
hideTeamBudgetForMembers: ctx.teamBudgetVisibility.hideTeamBudgetForMembers,
|
|
10056
|
-
hideTeamBudgetForManagers: ctx.teamBudgetVisibility.hideTeamBudgetForManagers
|
|
10058
|
+
hideTeamBudgetForManagers: ctx.teamBudgetVisibility.hideTeamBudgetForManagers,
|
|
10059
|
+
serviceKeyPrefixes: ctx.serviceKeyPrefixes
|
|
10057
10060
|
}
|
|
10058
10061
|
});
|
|
10059
10062
|
});
|
|
@@ -11521,6 +11524,22 @@ function registerTeamUsageRoutes(router, ctx) {
|
|
|
11521
11524
|
teamBudgetVisibility
|
|
11522
11525
|
} = ctx;
|
|
11523
11526
|
const requireUser = createRequireUser(ctx);
|
|
11527
|
+
async function isTeamManager(team, tokenEntityRef) {
|
|
11528
|
+
const owningGroup = typeof team.metadata?.owning_group === "string" ? team.metadata.owning_group : void 0;
|
|
11529
|
+
if (ctx.teamMgmtEnabled && owningGroup && await isUserMemberOfGroup(tokenEntityRef, owningGroup, catalogClient, auth, logger)) {
|
|
11530
|
+
return true;
|
|
11531
|
+
}
|
|
11532
|
+
const suffix = ctx.memberBreakdownCfg.leadGroupSuffix;
|
|
11533
|
+
if (!suffix || !team.team_alias) return false;
|
|
11534
|
+
const slug = team.team_alias.toLowerCase().replace(/[^a-z0-9\-_.]/g, "-");
|
|
11535
|
+
return isUserMemberOfGroup(
|
|
11536
|
+
tokenEntityRef,
|
|
11537
|
+
`group:default/${slug}${suffix}`,
|
|
11538
|
+
catalogClient,
|
|
11539
|
+
auth,
|
|
11540
|
+
logger
|
|
11541
|
+
);
|
|
11542
|
+
}
|
|
11524
11543
|
router.get("/teams/:teamId/usage", requireUser, async (req, res) => {
|
|
11525
11544
|
try {
|
|
11526
11545
|
const { teamId } = req.params;
|
|
@@ -11533,20 +11552,11 @@ function registerTeamUsageRoutes(router, ctx) {
|
|
|
11533
11552
|
const userInfo = await getProvisionedUser(ctx, res);
|
|
11534
11553
|
const isMember = userInfo?.teams?.includes(teamId) ?? false;
|
|
11535
11554
|
let isManager = false;
|
|
11536
|
-
const managerCheckNeeded = ctx.teamMgmtEnabled && (!isMember || teamBudgetVisibility.hideTeamBudgetForMembers !== teamBudgetVisibility.hideTeamBudgetForManagers);
|
|
11555
|
+
const managerCheckNeeded = (ctx.teamMgmtEnabled || !!ctx.memberBreakdownCfg.leadGroupSuffix) && (!isMember || teamBudgetVisibility.hideTeamBudgetForMembers !== teamBudgetVisibility.hideTeamBudgetForManagers);
|
|
11537
11556
|
if (managerCheckNeeded) {
|
|
11538
11557
|
try {
|
|
11539
11558
|
const team = await withTeamFetchRetry(() => client.getTeamInfo(teamId));
|
|
11540
|
-
|
|
11541
|
-
if (owningGroup) {
|
|
11542
|
-
isManager = await isUserMemberOfGroup(
|
|
11543
|
-
tokenEntityRef,
|
|
11544
|
-
owningGroup,
|
|
11545
|
-
catalogClient,
|
|
11546
|
-
auth,
|
|
11547
|
-
logger
|
|
11548
|
-
);
|
|
11549
|
-
}
|
|
11559
|
+
isManager = await isTeamManager(team, tokenEntityRef);
|
|
11550
11560
|
} catch (err) {
|
|
11551
11561
|
const message2 = err instanceof Error ? err.message : String(err);
|
|
11552
11562
|
logger.warn(`Team-manager check failed: ${message2}`);
|
|
@@ -11600,8 +11610,7 @@ function registerTeamUsageRoutes(router, ctx) {
|
|
|
11600
11610
|
}
|
|
11601
11611
|
throw err;
|
|
11602
11612
|
}
|
|
11603
|
-
const
|
|
11604
|
-
const isManager = ctx.teamMgmtEnabled && !!owningGroup && await isUserMemberOfGroup(tokenEntityRef, owningGroup, catalogClient, auth, logger);
|
|
11613
|
+
const isManager = await isTeamManager(team, tokenEntityRef);
|
|
11605
11614
|
if (!isMember && !isManager) {
|
|
11606
11615
|
res.status(404).json({ error: "Team not found" });
|
|
11607
11616
|
return null;
|
|
@@ -13663,6 +13672,7 @@ async function createRouter(options) {
|
|
|
13663
13672
|
const userIdDomain = config.getOptionalString("litellm.userIdDomain");
|
|
13664
13673
|
const publicBaseUrl = config.getOptionalString("litellm.publicBaseUrl") ?? null;
|
|
13665
13674
|
const supportContact = config.getOptionalString("litellm.supportContact");
|
|
13675
|
+
const serviceKeyPrefixes = (config.getOptionalStringArray("litellm.display.serviceKeyPrefixes") ?? []).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
13666
13676
|
const baseClient = options.client ?? new LiteLLMClient({ baseUrl, masterKey });
|
|
13667
13677
|
const userInfoCacheTtlSeconds = config.getOptionalNumber("litellm.cache.userInfoTtlSeconds") ?? 10;
|
|
13668
13678
|
const client = userInfoCacheTtlSeconds > 0 ? withUserInfoCache(baseClient, { ttlMs: userInfoCacheTtlSeconds * 1e3 }) : baseClient;
|
|
@@ -13721,6 +13731,7 @@ async function createRouter(options) {
|
|
|
13721
13731
|
baseUrl,
|
|
13722
13732
|
publicBaseUrl,
|
|
13723
13733
|
supportContact,
|
|
13734
|
+
serviceKeyPrefixes,
|
|
13724
13735
|
userIdDomain,
|
|
13725
13736
|
provisioningEnabled,
|
|
13726
13737
|
provisioningDefaults,
|