@acarmisc/backstage-plugin-litellm-backend 0.17.1 → 0.17.3

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.
@@ -3,13 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerBridgeRoutes = registerBridgeRoutes;
4
4
  const provisioning_1 = require("../provisioning");
5
5
  const bridge_1 = require("../bridge");
6
+ const backstage_plugin_litellm_common_1 = require("@acarmisc/backstage-plugin-litellm-common");
6
7
  const keyService_1 = require("../services/keyService");
7
8
  const errors_1 = require("../errors");
8
- const backstage_plugin_litellm_common_1 = require("@acarmisc/backstage-plugin-litellm-common");
9
+ const backstage_plugin_litellm_common_2 = require("@acarmisc/backstage-plugin-litellm-common");
9
10
  function registerBridgeRoutes(router, ctx, bridgeOpts) {
10
11
  const { client, logger, userIdDomain, provisioningEnabled, provisioningDefaults, roleConfigs, allowUnlimitedBudget, teamRequired, keyValidationConfig, } = ctx;
11
12
  const { config } = bridgeOpts;
12
- const generateKeyInputSchema = (0, backstage_plugin_litellm_common_1.createGenerateKeyInputSchema)(ctx.keyValidationConfig);
13
+ const generateKeyInputSchema = (0, backstage_plugin_litellm_common_2.createGenerateKeyInputSchema)(ctx.keyValidationConfig);
13
14
  // ---------------------------------------------------------------------------
14
15
  // Bridge endpoints (CLI / Abby)
15
16
  //
@@ -91,6 +92,13 @@ function registerBridgeRoutes(router, ctx, bridgeOpts) {
91
92
  return;
92
93
  }
93
94
  const input = parseResult.data;
95
+ // CLI clients don't know the budget policy. When unlimited budgets are
96
+ // not allowed and the caller sent no max_budget, fall back to the
97
+ // provisioning default instead of rejecting the mint. An explicit
98
+ // value (including null) is left alone and validated as usual.
99
+ if (!allowUnlimitedBudget && input.max_budget === undefined) {
100
+ input.max_budget = provisioningDefaults.maxBudget;
101
+ }
94
102
  // ── Resolve user identity from verified claims ───────────────────────
95
103
  const userId = (0, bridge_1.resolveBridgeUserId)(claims, bridgeIdentity);
96
104
  // ── Create key via unified service ──────────────────────────────────
@@ -118,11 +126,42 @@ function registerBridgeRoutes(router, ctx, bridgeOpts) {
118
126
  handleBridgeError(error, res);
119
127
  }
120
128
  });
129
+ // Caller's LiteLLM identity: user id + team memberships. Lets CLI clients
130
+ // offer a team picker and mint keys under a valid team.
131
+ router.get('/bridge/user/info', async (req, res) => {
132
+ try {
133
+ const claims = await requireClaims(req);
134
+ const user = await (0, bridge_1.getOrProvisionUserFromClaims)(client, claims, provisioningEnabled, provisioningDefaults, logger, bridgeIdentity);
135
+ res.json({ user_id: user.user_id, teams: user.teams ?? [] });
136
+ }
137
+ catch (error) {
138
+ handleBridgeError(error, res);
139
+ }
140
+ });
141
+ // Model catalogue. With ?team_id=X the list is narrowed to the models the
142
+ // team may use (its `models` plus access groups); the caller must belong
143
+ // to that team. Without it the full catalogue is returned, as before.
121
144
  router.get('/bridge/models', async (req, res) => {
122
145
  try {
123
- await requireClaims(req); // authenticate only
146
+ const claims = await requireClaims(req);
147
+ const teamId = typeof req.query.team_id === 'string' ? req.query.team_id : '';
124
148
  const models = await client.listModels();
125
- res.json(models);
149
+ if (!teamId) {
150
+ res.json(models);
151
+ return;
152
+ }
153
+ const user = await (0, bridge_1.getOrProvisionUserFromClaims)(client, claims, provisioningEnabled, provisioningDefaults, logger, bridgeIdentity);
154
+ if (!(user.teams ?? []).includes(teamId)) {
155
+ res.status(403).json({ error: 'Access denied: team is not one of your teams', team_id: teamId });
156
+ return;
157
+ }
158
+ const team = await client.getTeamInfo(teamId);
159
+ const allowed = team.models;
160
+ if (!allowed || allowed.length === 0 || allowed.includes(backstage_plugin_litellm_common_1.ALL_PROXY_MODELS)) {
161
+ res.json(models);
162
+ return;
163
+ }
164
+ res.json(models.filter(m => (0, backstage_plugin_litellm_common_1.isModelAllowed)(m, allowed)));
126
165
  }
127
166
  catch (error) {
128
167
  handleBridgeError(error, res);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acarmisc/backstage-plugin-litellm-backend",
3
- "version": "0.17.1",
3
+ "version": "0.17.3",
4
4
  "description": "The Backstage backend plugin for LiteLLM governance",
5
5
  "backstage": {
6
6
  "role": "backend-plugin",