@acarmisc/backstage-plugin-litellm-backend 0.23.0 → 0.25.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 +39 -0
- package/dist/client.js +16 -15
- package/dist/index.cjs.js +325 -69
- package/dist/index.cjs.js.map +4 -4
- package/dist/langfuse.d.ts +32 -0
- package/dist/langfuse.js +18 -0
- package/dist/langfuseClient.d.ts +23 -0
- package/dist/langfuseClient.js +171 -0
- package/dist/plugin.js +1 -0
- package/dist/router.js +3 -0
- package/dist/routes/bridge.js +24 -0
- package/dist/routes/context.d.ts +2 -0
- package/dist/routes/insights.js +37 -0
- package/dist/services/keyService.js +27 -1
- package/dist/services/mcpAccess.d.ts +20 -0
- package/dist/services/mcpAccess.js +28 -0
- package/dist/types.cjs.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
package/dist/index.cjs.js
CHANGED
|
@@ -7982,20 +7982,20 @@ __export(index_exports, {
|
|
|
7982
7982
|
getOrProvisionUser: () => getOrProvisionUser,
|
|
7983
7983
|
getOrProvisionUserFromClaims: () => getOrProvisionUserFromClaims,
|
|
7984
7984
|
isTeamManagementEnabled: () => isTeamManagementEnabled,
|
|
7985
|
-
litellmAuditReadPermission: () =>
|
|
7986
|
-
litellmKeyCreatePermission: () =>
|
|
7987
|
-
litellmKeyManagePermission: () =>
|
|
7988
|
-
litellmKeyResetSpendPermission: () =>
|
|
7989
|
-
litellmKeyRevokePermission: () =>
|
|
7990
|
-
litellmKeyUnblockPermission: () =>
|
|
7991
|
-
litellmPermissions: () =>
|
|
7985
|
+
litellmAuditReadPermission: () => import_backstage_plugin_litellm_common13.litellmAuditReadPermission,
|
|
7986
|
+
litellmKeyCreatePermission: () => import_backstage_plugin_litellm_common13.litellmKeyCreatePermission,
|
|
7987
|
+
litellmKeyManagePermission: () => import_backstage_plugin_litellm_common13.litellmKeyManagePermission,
|
|
7988
|
+
litellmKeyResetSpendPermission: () => import_backstage_plugin_litellm_common13.litellmKeyResetSpendPermission,
|
|
7989
|
+
litellmKeyRevokePermission: () => import_backstage_plugin_litellm_common13.litellmKeyRevokePermission,
|
|
7990
|
+
litellmKeyUnblockPermission: () => import_backstage_plugin_litellm_common13.litellmKeyUnblockPermission,
|
|
7991
|
+
litellmPermissions: () => import_backstage_plugin_litellm_common13.litellmPermissions,
|
|
7992
7992
|
litellmPlugin: () => litellmPlugin,
|
|
7993
|
-
litellmTeamCreatePermission: () =>
|
|
7994
|
-
litellmTeamDeletePermission: () =>
|
|
7995
|
-
litellmTeamKnowledgebaseManagePermission: () =>
|
|
7996
|
-
litellmTeamManagePermission: () =>
|
|
7997
|
-
litellmTeamMcpManagePermission: () =>
|
|
7998
|
-
litellmTeamMembersManagePermission: () =>
|
|
7993
|
+
litellmTeamCreatePermission: () => import_backstage_plugin_litellm_common13.litellmTeamCreatePermission,
|
|
7994
|
+
litellmTeamDeletePermission: () => import_backstage_plugin_litellm_common13.litellmTeamDeletePermission,
|
|
7995
|
+
litellmTeamKnowledgebaseManagePermission: () => import_backstage_plugin_litellm_common13.litellmTeamKnowledgebaseManagePermission,
|
|
7996
|
+
litellmTeamManagePermission: () => import_backstage_plugin_litellm_common13.litellmTeamManagePermission,
|
|
7997
|
+
litellmTeamMcpManagePermission: () => import_backstage_plugin_litellm_common13.litellmTeamMcpManagePermission,
|
|
7998
|
+
litellmTeamMembersManagePermission: () => import_backstage_plugin_litellm_common13.litellmTeamMembersManagePermission,
|
|
7999
7999
|
newDefaultVerifier: () => newDefaultVerifier,
|
|
8000
8000
|
provisionUser: () => provisionUser,
|
|
8001
8001
|
readBridgeConfig: () => readBridgeConfig,
|
|
@@ -8691,25 +8691,33 @@ var LiteLLMClient = class {
|
|
|
8691
8691
|
return this.normalizeSpendLogs(raw, page, pageSize);
|
|
8692
8692
|
}
|
|
8693
8693
|
normalizeSpendLogs(raw, page, pageSize) {
|
|
8694
|
-
const rows =
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8694
|
+
const rows = [];
|
|
8695
|
+
if (Array.isArray(raw)) rows.push(...raw);
|
|
8696
|
+
else if (Array.isArray(raw?.data)) rows.push(...raw.data);
|
|
8697
|
+
else if (Array.isArray(raw?.results)) rows.push(...raw.results);
|
|
8698
|
+
else if (Array.isArray(raw?.logs)) rows.push(...raw.logs);
|
|
8699
|
+
const toNum2 = (v, fallback = 0) => {
|
|
8700
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : fallback;
|
|
8701
|
+
if (typeof v === "string" && v.trim() !== "") {
|
|
8702
|
+
const n = Number(v);
|
|
8703
|
+
return Number.isFinite(n) ? n : fallback;
|
|
8704
|
+
}
|
|
8705
|
+
return fallback;
|
|
8698
8706
|
};
|
|
8699
8707
|
const data = rows.map((r) => {
|
|
8700
|
-
const promptTokens =
|
|
8708
|
+
const promptTokens = toNum2(
|
|
8701
8709
|
r?.prompt_tokens ?? r?.promptTokens ?? r?.input_tokens
|
|
8702
8710
|
);
|
|
8703
|
-
const completionTokens =
|
|
8711
|
+
const completionTokens = toNum2(
|
|
8704
8712
|
r?.completion_tokens ?? r?.completionTokens ?? r?.output_tokens
|
|
8705
8713
|
);
|
|
8706
8714
|
return {
|
|
8707
8715
|
requestId: String(r?.request_id ?? r?.requestId ?? r?.id ?? ""),
|
|
8708
8716
|
model: String(r?.model ?? r?.model_name ?? ""),
|
|
8709
|
-
spend:
|
|
8717
|
+
spend: toNum2(r?.spend ?? r?.cost),
|
|
8710
8718
|
promptTokens,
|
|
8711
8719
|
completionTokens,
|
|
8712
|
-
totalTokens:
|
|
8720
|
+
totalTokens: toNum2(
|
|
8713
8721
|
r?.total_tokens ?? r?.totalTokens,
|
|
8714
8722
|
promptTokens + completionTokens
|
|
8715
8723
|
),
|
|
@@ -8725,7 +8733,7 @@ var LiteLLMClient = class {
|
|
|
8725
8733
|
});
|
|
8726
8734
|
return {
|
|
8727
8735
|
data,
|
|
8728
|
-
total:
|
|
8736
|
+
total: toNum2(raw?.total ?? raw?.total_count, data.length),
|
|
8729
8737
|
page,
|
|
8730
8738
|
pageSize,
|
|
8731
8739
|
truncated: data.length >= pageSize
|
|
@@ -9159,7 +9167,7 @@ async function resolveUserRole(userEntityRef, roleConfigs, catalogClient, auth,
|
|
|
9159
9167
|
}
|
|
9160
9168
|
|
|
9161
9169
|
// src/router.ts
|
|
9162
|
-
var
|
|
9170
|
+
var import_backstage_plugin_litellm_common11 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
9163
9171
|
|
|
9164
9172
|
// src/teamAdmin.ts
|
|
9165
9173
|
var import_plugin_permission_common = __toESM(require_index_cjs2());
|
|
@@ -9522,6 +9530,21 @@ function redactTeamMemberUsage(usage) {
|
|
|
9522
9530
|
};
|
|
9523
9531
|
}
|
|
9524
9532
|
|
|
9533
|
+
// src/langfuse.ts
|
|
9534
|
+
function readLangfuseConfig(config) {
|
|
9535
|
+
const enabled = config.getOptionalBoolean("litellm.langfuse.enabled") ?? false;
|
|
9536
|
+
return {
|
|
9537
|
+
enabled,
|
|
9538
|
+
baseUrl: config.getOptionalString("litellm.langfuse.baseUrl") ?? null,
|
|
9539
|
+
publicKey: config.getOptionalString("litellm.langfuse.publicKey") ?? null,
|
|
9540
|
+
secretKey: config.getOptionalString("litellm.langfuse.secretKey") ?? null,
|
|
9541
|
+
userIdField: config.getOptionalString("litellm.langfuse.userIdField") ?? "userId"
|
|
9542
|
+
};
|
|
9543
|
+
}
|
|
9544
|
+
function isLangfuseConfigured(cfg) {
|
|
9545
|
+
return cfg.enabled && !!cfg.baseUrl && !!cfg.publicKey && !!cfg.secretKey;
|
|
9546
|
+
}
|
|
9547
|
+
|
|
9525
9548
|
// src/routes/middleware/withUser.ts
|
|
9526
9549
|
function createRequireUser(ctx) {
|
|
9527
9550
|
return async (req, res, next) => {
|
|
@@ -10045,7 +10068,7 @@ function registerConfigRoutes(router, ctx) {
|
|
|
10045
10068
|
}
|
|
10046
10069
|
|
|
10047
10070
|
// src/routes/keys.ts
|
|
10048
|
-
var
|
|
10071
|
+
var import_backstage_plugin_litellm_common4 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
10049
10072
|
|
|
10050
10073
|
// src/services/modelAccess.ts
|
|
10051
10074
|
var import_backstage_plugin_litellm_common2 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
@@ -10067,6 +10090,18 @@ async function findDisallowedModels(client, requested, allowed) {
|
|
|
10067
10090
|
);
|
|
10068
10091
|
}
|
|
10069
10092
|
|
|
10093
|
+
// src/services/mcpAccess.ts
|
|
10094
|
+
var import_backstage_plugin_litellm_common3 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
10095
|
+
var NO_MCP_SERVERS = "no-mcp-servers";
|
|
10096
|
+
function teamMcpServers(team) {
|
|
10097
|
+
const listed = team?.object_permission?.mcp_servers ?? [];
|
|
10098
|
+
return [...new Set(listed)].filter((n) => import_backstage_plugin_litellm_common3.MCP_SERVER_NAME_RE.test(n));
|
|
10099
|
+
}
|
|
10100
|
+
function findDisallowedMcpServers(requested, allowed) {
|
|
10101
|
+
const ok = new Set(allowed);
|
|
10102
|
+
return [...new Set(requested)].filter((n) => !ok.has(n));
|
|
10103
|
+
}
|
|
10104
|
+
|
|
10070
10105
|
// src/services/keyService.ts
|
|
10071
10106
|
var KeyServiceError = class extends Error {
|
|
10072
10107
|
constructor(status, body, message2) {
|
|
@@ -10108,10 +10143,12 @@ async function createKeyForUser(user, input, ctx) {
|
|
|
10108
10143
|
);
|
|
10109
10144
|
}
|
|
10110
10145
|
}
|
|
10146
|
+
let teamPromise;
|
|
10147
|
+
const getTeam = (teamId) => teamPromise ??= client.getTeamInfo(teamId);
|
|
10111
10148
|
if (input.models && input.models.length > 0) {
|
|
10112
10149
|
let allowedModels = [];
|
|
10113
10150
|
if (input.team_id) {
|
|
10114
|
-
const teamInfo = await
|
|
10151
|
+
const teamInfo = await getTeam(input.team_id);
|
|
10115
10152
|
allowedModels = teamInfo?.models ?? [];
|
|
10116
10153
|
} else {
|
|
10117
10154
|
allowedModels = userInfo?.models ?? [];
|
|
@@ -10144,6 +10181,19 @@ async function createKeyForUser(user, input, ctx) {
|
|
|
10144
10181
|
...clientMetadata,
|
|
10145
10182
|
...serverMetadata
|
|
10146
10183
|
};
|
|
10184
|
+
const mcpServers = input.mcp_servers ? [...new Set(input.mcp_servers)] : void 0;
|
|
10185
|
+
if (mcpServers && mcpServers.length > 0) {
|
|
10186
|
+
if (!input.team_id) {
|
|
10187
|
+
throw new KeyServiceError(400, { error: "team_id is required when mcp_servers is set" });
|
|
10188
|
+
}
|
|
10189
|
+
const disallowed = findDisallowedMcpServers(mcpServers, teamMcpServers(await getTeam(input.team_id)));
|
|
10190
|
+
if (disallowed.length > 0) {
|
|
10191
|
+
throw new KeyServiceError(403, {
|
|
10192
|
+
error: "One or more requested MCP servers are not allowed",
|
|
10193
|
+
disallowed_mcp_servers: disallowed
|
|
10194
|
+
});
|
|
10195
|
+
}
|
|
10196
|
+
}
|
|
10147
10197
|
const upstreamRequest = {
|
|
10148
10198
|
alias: input.alias,
|
|
10149
10199
|
user_id: userInfo.user_id
|
|
@@ -10172,6 +10222,11 @@ async function createKeyForUser(user, input, ctx) {
|
|
|
10172
10222
|
if (input.key_type !== void 0) {
|
|
10173
10223
|
upstreamRequest.key_type = input.key_type;
|
|
10174
10224
|
}
|
|
10225
|
+
if (mcpServers) {
|
|
10226
|
+
upstreamRequest.object_permission = {
|
|
10227
|
+
mcp_servers: mcpServers.length > 0 ? mcpServers : [NO_MCP_SERVERS]
|
|
10228
|
+
};
|
|
10229
|
+
}
|
|
10175
10230
|
upstreamRequest.metadata = enrichedMetadata;
|
|
10176
10231
|
const result = await client.generateKey(upstreamRequest);
|
|
10177
10232
|
logger.info("key.generate", { userId: userInfo.user_id, keyAlias: input.alias });
|
|
@@ -10194,8 +10249,8 @@ function registerKeysRoutes(router, ctx) {
|
|
|
10194
10249
|
assertPermission,
|
|
10195
10250
|
sendPermissionDenied
|
|
10196
10251
|
} = ctx;
|
|
10197
|
-
const generateKeyInputSchema = (0,
|
|
10198
|
-
const updateKeyInputSchema = (0,
|
|
10252
|
+
const generateKeyInputSchema = (0, import_backstage_plugin_litellm_common4.createGenerateKeyInputSchema)(keyValidationConfig);
|
|
10253
|
+
const updateKeyInputSchema = (0, import_backstage_plugin_litellm_common4.createUpdateKeyInputSchema)(keyValidationConfig);
|
|
10199
10254
|
router.get("/user/info", async (_req, res) => {
|
|
10200
10255
|
try {
|
|
10201
10256
|
const tokenEntityRef = res.locals.tokenEntityRef;
|
|
@@ -10232,8 +10287,8 @@ function registerKeysRoutes(router, ctx) {
|
|
|
10232
10287
|
});
|
|
10233
10288
|
router.post("/keys/prune-expired", async (_req, res) => {
|
|
10234
10289
|
try {
|
|
10235
|
-
if (!await assertPermission(_req,
|
|
10236
|
-
sendPermissionDenied(res,
|
|
10290
|
+
if (!await assertPermission(_req, import_backstage_plugin_litellm_common4.litellmKeyRevokePermission)) {
|
|
10291
|
+
sendPermissionDenied(res, import_backstage_plugin_litellm_common4.litellmKeyRevokePermission);
|
|
10237
10292
|
return;
|
|
10238
10293
|
}
|
|
10239
10294
|
await getProvisionedUser(ctx, res);
|
|
@@ -10297,8 +10352,8 @@ function registerKeysRoutes(router, ctx) {
|
|
|
10297
10352
|
return;
|
|
10298
10353
|
}
|
|
10299
10354
|
const input = parseResult.data;
|
|
10300
|
-
if (!await assertPermission(req,
|
|
10301
|
-
sendPermissionDenied(res,
|
|
10355
|
+
if (!await assertPermission(req, import_backstage_plugin_litellm_common4.litellmKeyCreatePermission)) {
|
|
10356
|
+
sendPermissionDenied(res, import_backstage_plugin_litellm_common4.litellmKeyCreatePermission);
|
|
10302
10357
|
return;
|
|
10303
10358
|
}
|
|
10304
10359
|
const tokenEntityRef = res.locals.tokenEntityRef;
|
|
@@ -10347,8 +10402,8 @@ function registerKeysRoutes(router, ctx) {
|
|
|
10347
10402
|
res.status(400).json({ error: "keyId is required" });
|
|
10348
10403
|
return;
|
|
10349
10404
|
}
|
|
10350
|
-
if (!await assertPermission(req,
|
|
10351
|
-
sendPermissionDenied(res,
|
|
10405
|
+
if (!await assertPermission(req, import_backstage_plugin_litellm_common4.litellmKeyManagePermission)) {
|
|
10406
|
+
sendPermissionDenied(res, import_backstage_plugin_litellm_common4.litellmKeyManagePermission);
|
|
10352
10407
|
return;
|
|
10353
10408
|
}
|
|
10354
10409
|
const { tokenEntityRef, userId, key } = await authorizeKeyAction(req, keyId);
|
|
@@ -10425,8 +10480,8 @@ function registerKeysRoutes(router, ctx) {
|
|
|
10425
10480
|
res.status(400).json({ error: "keyId is required" });
|
|
10426
10481
|
return;
|
|
10427
10482
|
}
|
|
10428
|
-
if (!await assertPermission(req,
|
|
10429
|
-
sendPermissionDenied(res,
|
|
10483
|
+
if (!await assertPermission(req, import_backstage_plugin_litellm_common4.litellmKeyRevokePermission)) {
|
|
10484
|
+
sendPermissionDenied(res, import_backstage_plugin_litellm_common4.litellmKeyRevokePermission);
|
|
10430
10485
|
return;
|
|
10431
10486
|
}
|
|
10432
10487
|
const { tokenEntityRef } = await authorizeKeyAction(req, keyId);
|
|
@@ -10440,8 +10495,8 @@ function registerKeysRoutes(router, ctx) {
|
|
|
10440
10495
|
router.post("/keys/:keyId/block", async (req, res) => {
|
|
10441
10496
|
try {
|
|
10442
10497
|
const { keyId } = req.params;
|
|
10443
|
-
if (!await assertPermission(req,
|
|
10444
|
-
sendPermissionDenied(res,
|
|
10498
|
+
if (!await assertPermission(req, import_backstage_plugin_litellm_common4.litellmKeyManagePermission)) {
|
|
10499
|
+
sendPermissionDenied(res, import_backstage_plugin_litellm_common4.litellmKeyManagePermission);
|
|
10445
10500
|
return;
|
|
10446
10501
|
}
|
|
10447
10502
|
const { tokenEntityRef, key } = await authorizeKeyAction(req, keyId);
|
|
@@ -10469,8 +10524,8 @@ function registerKeysRoutes(router, ctx) {
|
|
|
10469
10524
|
try {
|
|
10470
10525
|
const { keyId } = req.params;
|
|
10471
10526
|
const { tokenEntityRef, key } = await authorizeKeyAction(req, keyId);
|
|
10472
|
-
if (!await assertPermission(req,
|
|
10473
|
-
sendPermissionDenied(res,
|
|
10527
|
+
if (!await assertPermission(req, import_backstage_plugin_litellm_common4.litellmKeyUnblockPermission)) {
|
|
10528
|
+
sendPermissionDenied(res, import_backstage_plugin_litellm_common4.litellmKeyUnblockPermission);
|
|
10474
10529
|
return;
|
|
10475
10530
|
}
|
|
10476
10531
|
await client.unblockKey(keyId);
|
|
@@ -10498,8 +10553,8 @@ function registerKeysRoutes(router, ctx) {
|
|
|
10498
10553
|
});
|
|
10499
10554
|
return;
|
|
10500
10555
|
}
|
|
10501
|
-
if (!await assertPermission(req,
|
|
10502
|
-
sendPermissionDenied(res,
|
|
10556
|
+
if (!await assertPermission(req, import_backstage_plugin_litellm_common4.litellmKeyResetSpendPermission)) {
|
|
10557
|
+
sendPermissionDenied(res, import_backstage_plugin_litellm_common4.litellmKeyResetSpendPermission);
|
|
10503
10558
|
return;
|
|
10504
10559
|
}
|
|
10505
10560
|
const { tokenEntityRef } = await authorizeKeyAction(req, keyId);
|
|
@@ -10531,8 +10586,8 @@ function registerKeysRoutes(router, ctx) {
|
|
|
10531
10586
|
res.status(403).json({ error: "Access denied: not a member of the audit group" });
|
|
10532
10587
|
return;
|
|
10533
10588
|
}
|
|
10534
|
-
if (!await assertPermission(req,
|
|
10535
|
-
sendPermissionDenied(res,
|
|
10589
|
+
if (!await assertPermission(req, import_backstage_plugin_litellm_common4.litellmAuditReadPermission)) {
|
|
10590
|
+
sendPermissionDenied(res, import_backstage_plugin_litellm_common4.litellmAuditReadPermission);
|
|
10536
10591
|
return;
|
|
10537
10592
|
}
|
|
10538
10593
|
try {
|
|
@@ -10584,7 +10639,7 @@ function registerKeysRoutes(router, ctx) {
|
|
|
10584
10639
|
}
|
|
10585
10640
|
|
|
10586
10641
|
// src/routes/opencode.ts
|
|
10587
|
-
var
|
|
10642
|
+
var import_backstage_plugin_litellm_common5 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
10588
10643
|
function isValidRedirectUri(uri) {
|
|
10589
10644
|
if (!uri) return false;
|
|
10590
10645
|
try {
|
|
@@ -10626,8 +10681,8 @@ function registerOpencodeRoutes(router, ctx) {
|
|
|
10626
10681
|
return null;
|
|
10627
10682
|
}
|
|
10628
10683
|
const redirectUri = redirectUriParam;
|
|
10629
|
-
if (!await assertPermission(req,
|
|
10630
|
-
sendPermissionDenied(res,
|
|
10684
|
+
if (!await assertPermission(req, import_backstage_plugin_litellm_common5.litellmKeyCreatePermission)) {
|
|
10685
|
+
sendPermissionDenied(res, import_backstage_plugin_litellm_common5.litellmKeyCreatePermission);
|
|
10631
10686
|
return null;
|
|
10632
10687
|
}
|
|
10633
10688
|
const computedUserId = toLiteLLMUserId(tokenEntityRef, userIdDomain);
|
|
@@ -10795,7 +10850,7 @@ function registerOpencodeRoutes(router, ctx) {
|
|
|
10795
10850
|
}
|
|
10796
10851
|
|
|
10797
10852
|
// src/routes/teams.ts
|
|
10798
|
-
var
|
|
10853
|
+
var import_backstage_plugin_litellm_common6 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
10799
10854
|
|
|
10800
10855
|
// src/http/respondTeam.ts
|
|
10801
10856
|
function respondTeam(res, team, ctx, caller) {
|
|
@@ -10890,7 +10945,7 @@ function registerTeamsRoutes(router, ctx) {
|
|
|
10890
10945
|
permissions,
|
|
10891
10946
|
catalogClient,
|
|
10892
10947
|
teamAdminGroup: teamAdminCfg.group,
|
|
10893
|
-
permission:
|
|
10948
|
+
permission: import_backstage_plugin_litellm_common6.litellmTeamCreatePermission,
|
|
10894
10949
|
logger
|
|
10895
10950
|
});
|
|
10896
10951
|
if (!check.ok) {
|
|
@@ -10964,7 +11019,7 @@ function registerTeamsRoutes(router, ctx) {
|
|
|
10964
11019
|
const authz = await authorizeTeamSubresource(
|
|
10965
11020
|
req,
|
|
10966
11021
|
res,
|
|
10967
|
-
|
|
11022
|
+
import_backstage_plugin_litellm_common6.litellmTeamManagePermission
|
|
10968
11023
|
);
|
|
10969
11024
|
if (!authz) return;
|
|
10970
11025
|
const { teamId, owningGroup, actor, team: existing } = authz;
|
|
@@ -11005,7 +11060,7 @@ function registerTeamsRoutes(router, ctx) {
|
|
|
11005
11060
|
const authz = await authorizeTeamSubresource(
|
|
11006
11061
|
req,
|
|
11007
11062
|
res,
|
|
11008
|
-
|
|
11063
|
+
import_backstage_plugin_litellm_common6.litellmTeamDeletePermission
|
|
11009
11064
|
);
|
|
11010
11065
|
if (!authz) return;
|
|
11011
11066
|
const { teamId, owningGroup, actor } = authz;
|
|
@@ -11046,7 +11101,7 @@ function registerTeamsRoutes(router, ctx) {
|
|
|
11046
11101
|
permissions,
|
|
11047
11102
|
catalogClient,
|
|
11048
11103
|
teamAdminGroup: teamAdminCfg.group,
|
|
11049
|
-
permission:
|
|
11104
|
+
permission: import_backstage_plugin_litellm_common6.litellmTeamManagePermission,
|
|
11050
11105
|
logger
|
|
11051
11106
|
});
|
|
11052
11107
|
if (!check.ok) {
|
|
@@ -11069,7 +11124,7 @@ function registerTeamsRoutes(router, ctx) {
|
|
|
11069
11124
|
const authz = await authorizeTeamSubresource(
|
|
11070
11125
|
req,
|
|
11071
11126
|
res,
|
|
11072
|
-
|
|
11127
|
+
import_backstage_plugin_litellm_common6.litellmTeamMembersManagePermission,
|
|
11073
11128
|
{ allowTeamRole: true }
|
|
11074
11129
|
);
|
|
11075
11130
|
if (!authz) return;
|
|
@@ -11174,7 +11229,7 @@ function registerTeamsRoutes(router, ctx) {
|
|
|
11174
11229
|
const authz = await authorizeTeamSubresource(
|
|
11175
11230
|
req,
|
|
11176
11231
|
res,
|
|
11177
|
-
|
|
11232
|
+
import_backstage_plugin_litellm_common6.litellmTeamMembersManagePermission,
|
|
11178
11233
|
{ allowTeamRole: true }
|
|
11179
11234
|
);
|
|
11180
11235
|
if (!authz) return;
|
|
@@ -11222,7 +11277,7 @@ function registerTeamsRoutes(router, ctx) {
|
|
|
11222
11277
|
}
|
|
11223
11278
|
|
|
11224
11279
|
// src/routes/objectPermissions.ts
|
|
11225
|
-
var
|
|
11280
|
+
var import_backstage_plugin_litellm_common7 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
11226
11281
|
function registerObjectPermissionsRoutes(router, ctx) {
|
|
11227
11282
|
const {
|
|
11228
11283
|
client,
|
|
@@ -11247,7 +11302,7 @@ function registerObjectPermissionsRoutes(router, ctx) {
|
|
|
11247
11302
|
permissions,
|
|
11248
11303
|
catalogClient,
|
|
11249
11304
|
teamAdminGroup: teamAdminCfg.group,
|
|
11250
|
-
permission:
|
|
11305
|
+
permission: import_backstage_plugin_litellm_common7.litellmTeamKnowledgebaseManagePermission,
|
|
11251
11306
|
logger
|
|
11252
11307
|
});
|
|
11253
11308
|
if (!check.ok) {
|
|
@@ -11269,7 +11324,7 @@ function registerObjectPermissionsRoutes(router, ctx) {
|
|
|
11269
11324
|
const authz = await authorizeTeamSubresource(
|
|
11270
11325
|
req,
|
|
11271
11326
|
res,
|
|
11272
|
-
|
|
11327
|
+
import_backstage_plugin_litellm_common7.litellmTeamKnowledgebaseManagePermission
|
|
11273
11328
|
);
|
|
11274
11329
|
if (!authz) return;
|
|
11275
11330
|
const { teamId, owningGroup, actor, team } = authz;
|
|
@@ -11329,7 +11384,7 @@ function registerObjectPermissionsRoutes(router, ctx) {
|
|
|
11329
11384
|
permissions,
|
|
11330
11385
|
catalogClient,
|
|
11331
11386
|
teamAdminGroup: teamAdminCfg.group,
|
|
11332
|
-
permission:
|
|
11387
|
+
permission: import_backstage_plugin_litellm_common7.litellmTeamMcpManagePermission,
|
|
11333
11388
|
logger
|
|
11334
11389
|
});
|
|
11335
11390
|
if (!check.ok) {
|
|
@@ -11351,7 +11406,7 @@ function registerObjectPermissionsRoutes(router, ctx) {
|
|
|
11351
11406
|
const authz = await authorizeTeamSubresource(
|
|
11352
11407
|
req,
|
|
11353
11408
|
res,
|
|
11354
|
-
|
|
11409
|
+
import_backstage_plugin_litellm_common7.litellmTeamMcpManagePermission
|
|
11355
11410
|
);
|
|
11356
11411
|
if (!authz) return;
|
|
11357
11412
|
const { teamId, owningGroup, actor, team } = authz;
|
|
@@ -11404,7 +11459,7 @@ function registerObjectPermissionsRoutes(router, ctx) {
|
|
|
11404
11459
|
}
|
|
11405
11460
|
|
|
11406
11461
|
// src/routes/teamUsage.ts
|
|
11407
|
-
var
|
|
11462
|
+
var import_backstage_plugin_litellm_common8 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
11408
11463
|
function registerTeamUsageRoutes(router, ctx) {
|
|
11409
11464
|
const {
|
|
11410
11465
|
client,
|
|
@@ -11502,7 +11557,7 @@ function registerTeamUsageRoutes(router, ctx) {
|
|
|
11502
11557
|
}
|
|
11503
11558
|
const role = team.members_with_roles?.find((m) => m.user_id === callerId)?.role;
|
|
11504
11559
|
const viaRole = isMember && !!role && cfg.viewerRoles.includes(role);
|
|
11505
|
-
const allowed = isManager || viaRole || isMember && cfg.permissionEnabled && await ctx.assertPermission(req,
|
|
11560
|
+
const allowed = isManager || viaRole || isMember && cfg.permissionEnabled && await ctx.assertPermission(req, import_backstage_plugin_litellm_common8.litellmTeamUsageReadPermission);
|
|
11506
11561
|
if (!allowed) {
|
|
11507
11562
|
res.status(403).json({
|
|
11508
11563
|
error: "Access denied: viewing per-member usage needs a team role or permission"
|
|
@@ -11558,6 +11613,143 @@ function registerTeamUsageRoutes(router, ctx) {
|
|
|
11558
11613
|
}
|
|
11559
11614
|
}
|
|
11560
11615
|
|
|
11616
|
+
// src/langfuseClient.ts
|
|
11617
|
+
var DEFAULT_TIMEOUT2 = 15e3;
|
|
11618
|
+
var LangfuseUpstreamError = class extends Error {
|
|
11619
|
+
constructor(status, statusText) {
|
|
11620
|
+
super(`Langfuse API error: ${status} ${statusText}`);
|
|
11621
|
+
this.status = status;
|
|
11622
|
+
}
|
|
11623
|
+
};
|
|
11624
|
+
async function queryMetrics(baseUrl, publicKey, secretKey, queryObj, timeout = DEFAULT_TIMEOUT2) {
|
|
11625
|
+
const controller = new AbortController();
|
|
11626
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
11627
|
+
try {
|
|
11628
|
+
const url = `${baseUrl.replace(/\/$/, "")}/api/public/v2/metrics?query=${encodeURIComponent(
|
|
11629
|
+
JSON.stringify(queryObj)
|
|
11630
|
+
)}`;
|
|
11631
|
+
const response = await fetch(url, {
|
|
11632
|
+
signal: controller.signal,
|
|
11633
|
+
headers: {
|
|
11634
|
+
Authorization: `Basic ${Buffer.from(`${publicKey}:${secretKey}`).toString("base64")}`
|
|
11635
|
+
}
|
|
11636
|
+
});
|
|
11637
|
+
if (!response.ok) {
|
|
11638
|
+
throw new LangfuseUpstreamError(response.status, response.statusText);
|
|
11639
|
+
}
|
|
11640
|
+
return response.json();
|
|
11641
|
+
} finally {
|
|
11642
|
+
clearTimeout(timeoutId);
|
|
11643
|
+
}
|
|
11644
|
+
}
|
|
11645
|
+
function toNum(v) {
|
|
11646
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
11647
|
+
if (typeof v === "string" && v.trim() !== "") {
|
|
11648
|
+
const n = Number(v);
|
|
11649
|
+
return Number.isFinite(n) ? n : null;
|
|
11650
|
+
}
|
|
11651
|
+
return null;
|
|
11652
|
+
}
|
|
11653
|
+
function pick(row, names) {
|
|
11654
|
+
const scopes = [row, row?.metrics, row?.dimensions];
|
|
11655
|
+
for (const scope of scopes) {
|
|
11656
|
+
if (!scope || typeof scope !== "object") continue;
|
|
11657
|
+
for (const name of names) {
|
|
11658
|
+
const n = toNum(scope[name]);
|
|
11659
|
+
if (n !== null) return n;
|
|
11660
|
+
}
|
|
11661
|
+
}
|
|
11662
|
+
return null;
|
|
11663
|
+
}
|
|
11664
|
+
function pickText(row, names) {
|
|
11665
|
+
const scopes = [row, row?.metrics, row?.dimensions];
|
|
11666
|
+
for (const scope of scopes) {
|
|
11667
|
+
if (!scope || typeof scope !== "object") continue;
|
|
11668
|
+
for (const name of names) {
|
|
11669
|
+
const v = scope[name];
|
|
11670
|
+
if (typeof v === "string" && v) return v;
|
|
11671
|
+
}
|
|
11672
|
+
}
|
|
11673
|
+
return null;
|
|
11674
|
+
}
|
|
11675
|
+
function metricRows(body) {
|
|
11676
|
+
if (Array.isArray(body)) return body;
|
|
11677
|
+
if (Array.isArray(body?.data)) return body.data;
|
|
11678
|
+
if (Array.isArray(body?.results)) return body.results;
|
|
11679
|
+
if (Array.isArray(body?.rows)) return body.rows;
|
|
11680
|
+
return [];
|
|
11681
|
+
}
|
|
11682
|
+
function parseObservations(body) {
|
|
11683
|
+
const byModel = metricRows(body).map((row) => ({
|
|
11684
|
+
model: pickText(row, ["providedModelName", "model", "modelName"]) ?? "",
|
|
11685
|
+
cost: pick(row, ["sum_totalCost", "totalCost", "sum_total_cost", "cost", "sum_cost"]) ?? 0,
|
|
11686
|
+
tokens: pick(row, ["sum_totalTokens", "totalTokens", "sum_total_tokens", "tokens"]) ?? 0,
|
|
11687
|
+
avgLatencyMs: pick(row, ["avg_latency", "latency", "avg_latency_ms", "latencyMs"]),
|
|
11688
|
+
requests: pick(row, ["count_id", "count", "id_count", "requests", "observations"]) ?? 0
|
|
11689
|
+
})).filter((r) => r.model !== "");
|
|
11690
|
+
byModel.sort((a, b) => b.cost - a.cost);
|
|
11691
|
+
return byModel;
|
|
11692
|
+
}
|
|
11693
|
+
function parseScores(body) {
|
|
11694
|
+
const scores = metricRows(body).map((row) => ({
|
|
11695
|
+
name: pickText(row, ["name", "scoreName"]) ?? "",
|
|
11696
|
+
avg: pick(row, ["avg_value", "value", "avg"]) ?? NaN,
|
|
11697
|
+
count: pick(row, ["count_id", "count", "id_count", "scores"]) ?? 0
|
|
11698
|
+
})).filter((s) => s.name !== "" && Number.isFinite(s.avg));
|
|
11699
|
+
scores.sort((a, b) => a.name.localeCompare(b.name));
|
|
11700
|
+
return scores;
|
|
11701
|
+
}
|
|
11702
|
+
function toTimestamp(day2, endOfDay) {
|
|
11703
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(day2)) {
|
|
11704
|
+
return endOfDay ? `${day2}T23:59:59.999Z` : `${day2}T00:00:00.000Z`;
|
|
11705
|
+
}
|
|
11706
|
+
return day2;
|
|
11707
|
+
}
|
|
11708
|
+
async function getQualitySummary(cfg, opts) {
|
|
11709
|
+
if (!isLangfuseConfigured(cfg)) return null;
|
|
11710
|
+
const { baseUrl, publicKey, secretKey, userIdField } = cfg;
|
|
11711
|
+
const fromTimestamp = toTimestamp(opts.from, false);
|
|
11712
|
+
const toTimestampValue = toTimestamp(opts.to, true);
|
|
11713
|
+
try {
|
|
11714
|
+
const observations = await queryMetrics(baseUrl, publicKey, secretKey, {
|
|
11715
|
+
view: "observations",
|
|
11716
|
+
metrics: [
|
|
11717
|
+
{ measure: "totalCost", aggregation: "SUM" },
|
|
11718
|
+
{ measure: "totalTokens", aggregation: "SUM" },
|
|
11719
|
+
{ measure: "latency", aggregation: "AVG" },
|
|
11720
|
+
{ measure: "id", aggregation: "COUNT" }
|
|
11721
|
+
],
|
|
11722
|
+
dimensions: [{ field: "providedModelName" }],
|
|
11723
|
+
filters: [
|
|
11724
|
+
{ column: userIdField, operator: "=", value: opts.userId, type: "string" }
|
|
11725
|
+
],
|
|
11726
|
+
fromTimestamp,
|
|
11727
|
+
toTimestamp: toTimestampValue,
|
|
11728
|
+
config: { row_limit: 50 }
|
|
11729
|
+
});
|
|
11730
|
+
let scores = [];
|
|
11731
|
+
try {
|
|
11732
|
+
const scoresBody = await queryMetrics(baseUrl, publicKey, secretKey, {
|
|
11733
|
+
view: "scores-numeric",
|
|
11734
|
+
metrics: [
|
|
11735
|
+
{ measure: "value", aggregation: "AVG" },
|
|
11736
|
+
{ measure: "id", aggregation: "COUNT" }
|
|
11737
|
+
],
|
|
11738
|
+
dimensions: [{ field: "name" }],
|
|
11739
|
+
fromTimestamp,
|
|
11740
|
+
toTimestamp: toTimestampValue,
|
|
11741
|
+
config: { row_limit: 50 }
|
|
11742
|
+
});
|
|
11743
|
+
scores = parseScores(scoresBody);
|
|
11744
|
+
} catch {
|
|
11745
|
+
scores = [];
|
|
11746
|
+
}
|
|
11747
|
+
return { configured: true, byModel: parseObservations(observations), scores };
|
|
11748
|
+
} catch {
|
|
11749
|
+
return null;
|
|
11750
|
+
}
|
|
11751
|
+
}
|
|
11752
|
+
|
|
11561
11753
|
// src/routes/insights.ts
|
|
11562
11754
|
function registerInsightsRoutes(router, ctx) {
|
|
11563
11755
|
const { client, catalogClient, auth, logger, auditGroup } = ctx;
|
|
@@ -11596,6 +11788,41 @@ function registerInsightsRoutes(router, ctx) {
|
|
|
11596
11788
|
sendError(res, error, logger, "fetch spend logs");
|
|
11597
11789
|
}
|
|
11598
11790
|
});
|
|
11791
|
+
router.get("/insights/quality", requireUser, async (req, res) => {
|
|
11792
|
+
try {
|
|
11793
|
+
const { start_date, end_date, userId } = req.query;
|
|
11794
|
+
if (!start_date || !end_date) {
|
|
11795
|
+
res.status(400).json({ error: "start_date and end_date are required" });
|
|
11796
|
+
return;
|
|
11797
|
+
}
|
|
11798
|
+
const tokenEntityRef = res.locals.tokenEntityRef;
|
|
11799
|
+
await getProvisionedUser(ctx, res);
|
|
11800
|
+
const callerId = res.locals.userId;
|
|
11801
|
+
const isAudit = auditGroup ? await isUserMemberOfGroup(
|
|
11802
|
+
tokenEntityRef,
|
|
11803
|
+
auditGroup,
|
|
11804
|
+
catalogClient,
|
|
11805
|
+
auth,
|
|
11806
|
+
logger
|
|
11807
|
+
) : false;
|
|
11808
|
+
if (!isLangfuseConfigured(ctx.langfuseCfg)) {
|
|
11809
|
+
res.json({ configured: false });
|
|
11810
|
+
return;
|
|
11811
|
+
}
|
|
11812
|
+
const summary = await getQualitySummary(ctx.langfuseCfg, {
|
|
11813
|
+
from: start_date,
|
|
11814
|
+
to: end_date,
|
|
11815
|
+
userId: isAudit ? userId || callerId : callerId
|
|
11816
|
+
});
|
|
11817
|
+
res.json(summary ?? { configured: false });
|
|
11818
|
+
} catch (error) {
|
|
11819
|
+
if (error instanceof ProvisioningError) {
|
|
11820
|
+
res.status(error.status).json(error.body);
|
|
11821
|
+
return;
|
|
11822
|
+
}
|
|
11823
|
+
sendError(res, error, logger, "fetch quality summary");
|
|
11824
|
+
}
|
|
11825
|
+
});
|
|
11599
11826
|
}
|
|
11600
11827
|
|
|
11601
11828
|
// node_modules/jose/dist/node/esm/runtime/base64url.js
|
|
@@ -13111,8 +13338,8 @@ async function bridgeListKeys(client, claims, provisioningEnabled, provisioningD
|
|
|
13111
13338
|
}
|
|
13112
13339
|
|
|
13113
13340
|
// src/routes/bridge.ts
|
|
13114
|
-
var import_backstage_plugin_litellm_common8 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
13115
13341
|
var import_backstage_plugin_litellm_common9 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
13342
|
+
var import_backstage_plugin_litellm_common10 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
13116
13343
|
function registerBridgeRoutes(router, ctx, bridgeOpts) {
|
|
13117
13344
|
const {
|
|
13118
13345
|
client,
|
|
@@ -13125,7 +13352,7 @@ function registerBridgeRoutes(router, ctx, bridgeOpts) {
|
|
|
13125
13352
|
keyValidationConfig
|
|
13126
13353
|
} = ctx;
|
|
13127
13354
|
const { config } = bridgeOpts;
|
|
13128
|
-
const generateKeyInputSchema = (0,
|
|
13355
|
+
const generateKeyInputSchema = (0, import_backstage_plugin_litellm_common10.createGenerateKeyInputSchema)(ctx.keyValidationConfig);
|
|
13129
13356
|
const bridgeCfg = readBridgeConfig(config);
|
|
13130
13357
|
const bridgeIdentity = {
|
|
13131
13358
|
userIdDomain,
|
|
@@ -13286,11 +13513,37 @@ function registerBridgeRoutes(router, ctx, bridgeOpts) {
|
|
|
13286
13513
|
}
|
|
13287
13514
|
const team = await client.getTeamInfo(teamId);
|
|
13288
13515
|
const allowed = team.models;
|
|
13289
|
-
if (!allowed || allowed.length === 0 || allowed.includes(
|
|
13516
|
+
if (!allowed || allowed.length === 0 || allowed.includes(import_backstage_plugin_litellm_common9.ALL_PROXY_MODELS)) {
|
|
13290
13517
|
res.json(models);
|
|
13291
13518
|
return;
|
|
13292
13519
|
}
|
|
13293
|
-
res.json(models.filter((m) => (0,
|
|
13520
|
+
res.json(models.filter((m) => (0, import_backstage_plugin_litellm_common9.isModelAllowed)(m, allowed)));
|
|
13521
|
+
} catch (error) {
|
|
13522
|
+
handleBridgeError(error, res);
|
|
13523
|
+
}
|
|
13524
|
+
});
|
|
13525
|
+
router.get("/bridge/mcp", async (req, res) => {
|
|
13526
|
+
try {
|
|
13527
|
+
const claims = await requireClaims(req);
|
|
13528
|
+
const teamId = typeof req.query.team_id === "string" ? req.query.team_id : "";
|
|
13529
|
+
if (!teamId) {
|
|
13530
|
+
res.status(400).json({ error: "team_id is required" });
|
|
13531
|
+
return;
|
|
13532
|
+
}
|
|
13533
|
+
const user = await getOrProvisionUserFromClaims(
|
|
13534
|
+
client,
|
|
13535
|
+
claims,
|
|
13536
|
+
provisioningEnabled,
|
|
13537
|
+
provisioningDefaults,
|
|
13538
|
+
logger,
|
|
13539
|
+
bridgeIdentity
|
|
13540
|
+
);
|
|
13541
|
+
if (!(user.teams ?? []).includes(teamId)) {
|
|
13542
|
+
res.status(403).json({ error: "Access denied: team is not one of your teams", team_id: teamId });
|
|
13543
|
+
return;
|
|
13544
|
+
}
|
|
13545
|
+
const names = teamMcpServers(await client.getTeamInfo(teamId));
|
|
13546
|
+
res.json(names.map((server_name) => ({ server_name })));
|
|
13294
13547
|
} catch (error) {
|
|
13295
13548
|
handleBridgeError(error, res);
|
|
13296
13549
|
}
|
|
@@ -13322,13 +13575,13 @@ async function createRouter(options) {
|
|
|
13322
13575
|
const keyMaxBudget = config.getOptionalNumber("litellm.keys.maxBudget") ?? 100;
|
|
13323
13576
|
const keyMaxTpm = config.getOptionalNumber("litellm.keys.maxTpm") ?? 1e5;
|
|
13324
13577
|
const keyMaxRpm = config.getOptionalNumber("litellm.keys.maxRpm") ?? 1e3;
|
|
13325
|
-
const keyAllowedDurations = config.getOptionalStringArray("litellm.keys.allowedDurations") ??
|
|
13578
|
+
const keyAllowedDurations = config.getOptionalStringArray("litellm.keys.allowedDurations") ?? import_backstage_plugin_litellm_common11.DEFAULT_KEY_DURATIONS;
|
|
13326
13579
|
const keyValidationConfig = {
|
|
13327
13580
|
maxBudget: keyMaxBudget,
|
|
13328
13581
|
maxTpm: keyMaxTpm,
|
|
13329
13582
|
maxRpm: keyMaxRpm,
|
|
13330
13583
|
allowedDurations: keyAllowedDurations,
|
|
13331
|
-
allowedBudgetDurations: config.getOptionalStringArray("litellm.keys.allowedBudgetDurations") ??
|
|
13584
|
+
allowedBudgetDurations: config.getOptionalStringArray("litellm.keys.allowedBudgetDurations") ?? import_backstage_plugin_litellm_common11.DEFAULT_KEY_BUDGET_DURATIONS
|
|
13332
13585
|
};
|
|
13333
13586
|
const teamMgmtEnabled = isTeamManagementEnabled(config);
|
|
13334
13587
|
const objectPermsEnabled = isObjectPermissionsEnabled(config);
|
|
@@ -13336,6 +13589,7 @@ async function createRouter(options) {
|
|
|
13336
13589
|
const teamBudgetVisibility = readTeamBudgetVisibility(config);
|
|
13337
13590
|
const opencodeCfg = readOpencodeConfig(config);
|
|
13338
13591
|
const memberBreakdownCfg = readMemberBreakdownConfig(config);
|
|
13592
|
+
const langfuseCfg = readLangfuseConfig(config);
|
|
13339
13593
|
const catalogClient = options.catalogClient ?? new import_catalog_client.CatalogClient({ discoveryApi: discovery });
|
|
13340
13594
|
if (provisioningEnabled) {
|
|
13341
13595
|
logger.info(
|
|
@@ -13382,6 +13636,7 @@ async function createRouter(options) {
|
|
|
13382
13636
|
teamBudgetVisibility,
|
|
13383
13637
|
opencodeCfg,
|
|
13384
13638
|
memberBreakdownCfg,
|
|
13639
|
+
langfuseCfg,
|
|
13385
13640
|
// Helper functions - defined inline below this object
|
|
13386
13641
|
authorizeKeyAction: async (req, keyId) => {
|
|
13387
13642
|
if (!req.res || !req.res.locals.tokenEntityRef || !req.res.locals.userId) {
|
|
@@ -13539,10 +13794,11 @@ async function createRouter(options) {
|
|
|
13539
13794
|
}
|
|
13540
13795
|
|
|
13541
13796
|
// src/plugin.ts
|
|
13542
|
-
var
|
|
13797
|
+
var import_backstage_plugin_litellm_common12 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
13543
13798
|
var BRIDGE_UNAUTHENTICATED_PATHS = [
|
|
13544
13799
|
"/bridge/health",
|
|
13545
13800
|
"/bridge/keys",
|
|
13801
|
+
"/bridge/mcp",
|
|
13546
13802
|
"/bridge/models",
|
|
13547
13803
|
"/bridge/user/info"
|
|
13548
13804
|
];
|
|
@@ -13568,7 +13824,7 @@ var litellmPlugin = (0, import_backend_plugin_api.createBackendPlugin)({
|
|
|
13568
13824
|
permissions,
|
|
13569
13825
|
permissionsRegistry
|
|
13570
13826
|
}) {
|
|
13571
|
-
permissionsRegistry.addPermissions(
|
|
13827
|
+
permissionsRegistry.addPermissions(import_backstage_plugin_litellm_common12.litellmPermissions);
|
|
13572
13828
|
const router = await createRouter({
|
|
13573
13829
|
config,
|
|
13574
13830
|
logger,
|
|
@@ -13588,7 +13844,7 @@ var litellmPlugin = (0, import_backend_plugin_api.createBackendPlugin)({
|
|
|
13588
13844
|
});
|
|
13589
13845
|
|
|
13590
13846
|
// src/index.ts
|
|
13591
|
-
var
|
|
13847
|
+
var import_backstage_plugin_litellm_common13 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
13592
13848
|
// Annotate the CommonJS export names for ESM import in node:
|
|
13593
13849
|
0 && (module.exports = {
|
|
13594
13850
|
KeycloakJWTVerifier,
|