@acarmisc/backstage-plugin-litellm-backend 0.10.0 → 0.11.1

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 CHANGED
@@ -184,8 +184,7 @@ export interface Config {
184
184
 
185
185
  /**
186
186
  * Hard USD ceiling for max_budget an admin may set on a team.
187
- * When omitted, no admin-settable budget is allowed unless
188
- * allowUnlimitedBudget is true.
187
+ * @default 1000
189
188
  * @visibility backend
190
189
  */
191
190
  maxBudgetCeiling?: number;
package/dist/client.js CHANGED
@@ -314,6 +314,7 @@ class LiteLLMClient {
314
314
  team_id: raw?.team_id ?? inner.team_id ?? fallbackId ?? '',
315
315
  team_alias: inner.team_alias ?? raw?.team_alias,
316
316
  max_budget: inner.max_budget ?? raw?.max_budget,
317
+ budget_duration: inner.budget_duration ?? raw?.budget_duration,
317
318
  spend: inner.spend ?? raw?.spend ?? 0,
318
319
  members_with_roles: inner.members_with_roles ?? raw?.members_with_roles,
319
320
  models: inner.models ?? raw?.models,
package/dist/index.cjs.js CHANGED
@@ -8301,6 +8301,7 @@ var LiteLLMClient = class {
8301
8301
  team_id: raw?.team_id ?? inner.team_id ?? fallbackId ?? "",
8302
8302
  team_alias: inner.team_alias ?? raw?.team_alias,
8303
8303
  max_budget: inner.max_budget ?? raw?.max_budget,
8304
+ budget_duration: inner.budget_duration ?? raw?.budget_duration,
8304
8305
  spend: inner.spend ?? raw?.spend ?? 0,
8305
8306
  members_with_roles: inner.members_with_roles ?? raw?.members_with_roles,
8306
8307
  models: inner.models ?? raw?.models,
@@ -10630,6 +10631,7 @@ var litellmPermissions = [
10630
10631
 
10631
10632
  // src/teamAdmin.ts
10632
10633
  var import_plugin_permission_common2 = __toESM(require_index_cjs2());
10634
+ var DEFAULT_TEAM_BUDGET_CEILING = 1e3;
10633
10635
  function isTeamManagementEnabled(config) {
10634
10636
  const permEnabled = config.getOptionalBoolean("permission.enabled") ?? false;
10635
10637
  const adminConfig = readTeamAdminConfig(config);
@@ -10689,9 +10691,7 @@ function readTeamAdminConfig(config) {
10689
10691
  allowedModelAccessGroups: config.getOptionalStringArray(
10690
10692
  "litellm.teamAdmin.allowedModelAccessGroups"
10691
10693
  ) ?? [],
10692
- maxBudgetCeiling: config.getOptionalNumber(
10693
- "litellm.teamAdmin.maxBudgetCeiling"
10694
- ),
10694
+ maxBudgetCeiling: config.getOptionalNumber("litellm.teamAdmin.maxBudgetCeiling") ?? DEFAULT_TEAM_BUDGET_CEILING,
10695
10695
  allowUnlimitedBudget: config.getOptionalBoolean("litellm.teamAdmin.allowUnlimitedBudget") ?? false,
10696
10696
  allowedVectorStores: config.getOptionalStringArray("litellm.teamAdmin.allowedVectorStores") ?? [],
10697
10697
  allowedMcpServers: config.getOptionalStringArray("litellm.teamAdmin.allowedMcpServers") ?? [],
@@ -10730,13 +10730,10 @@ function validateTeamWriteInput(input, cfg) {
10730
10730
  if (maxBudget === void 0) {
10731
10731
  return { ok: false, error: "max_budget is required" };
10732
10732
  }
10733
- if (cfg.maxBudgetCeiling === void 0) {
10734
- return { ok: false, error: "team budget ceiling is not configured (litellm.teamAdmin.maxBudgetCeiling)" };
10735
- }
10736
10733
  if (maxBudget > cfg.maxBudgetCeiling) {
10737
10734
  return { ok: false, error: `max_budget $${maxBudget} exceeds the ceiling $${cfg.maxBudgetCeiling}` };
10738
10735
  }
10739
- } else if (maxBudget !== void 0 && cfg.maxBudgetCeiling !== void 0 && maxBudget > cfg.maxBudgetCeiling) {
10736
+ } else if (maxBudget !== void 0 && maxBudget > cfg.maxBudgetCeiling) {
10740
10737
  return { ok: false, error: `max_budget $${maxBudget} exceeds the ceiling $${cfg.maxBudgetCeiling}` };
10741
10738
  }
10742
10739
  let budgetDuration = input.budget_duration;
@@ -10797,7 +10794,7 @@ function validateTeamPatchInput(input, cfg) {
10797
10794
  if (!Number.isFinite(input.max_budget) || input.max_budget <= 0) {
10798
10795
  return { ok: false, error: "max_budget must be a positive number" };
10799
10796
  }
10800
- if (cfg.maxBudgetCeiling !== void 0 && input.max_budget > cfg.maxBudgetCeiling) {
10797
+ if (input.max_budget > cfg.maxBudgetCeiling) {
10801
10798
  return { ok: false, error: `max_budget $${input.max_budget} exceeds the ceiling $${cfg.maxBudgetCeiling}` };
10802
10799
  }
10803
10800
  result.max_budget = input.max_budget;
@@ -10830,6 +10827,25 @@ function validateTeamPatchInput(input, cfg) {
10830
10827
 
10831
10828
  // src/router.ts
10832
10829
  var teamCreateInFlight = /* @__PURE__ */ new Map();
10830
+ var TEAM_FETCH_RETRY_DELAYS_MS = [100, 300, 900];
10831
+ function isRetryableTeamFetchError(err) {
10832
+ if (err instanceof LiteLLMUpstreamError) {
10833
+ return err.status >= 500;
10834
+ }
10835
+ return true;
10836
+ }
10837
+ async function withTeamFetchRetry(fn, delaysMs = TEAM_FETCH_RETRY_DELAYS_MS) {
10838
+ for (let attempt = 0; ; attempt++) {
10839
+ try {
10840
+ return await fn();
10841
+ } catch (err) {
10842
+ if (attempt >= delaysMs.length || !isRetryableTeamFetchError(err)) {
10843
+ throw err;
10844
+ }
10845
+ await new Promise((resolve) => setTimeout(resolve, delaysMs[attempt]));
10846
+ }
10847
+ }
10848
+ }
10833
10849
  async function createRouter(options) {
10834
10850
  const { config, logger, auth, discovery, permissions } = options;
10835
10851
  const baseUrl = config.getString("litellm.baseUrl");
@@ -10867,7 +10883,7 @@ async function createRouter(options) {
10867
10883
  keyGeneration: { allowUnlimitedBudget, teamRequired },
10868
10884
  teamManagement: {
10869
10885
  enabled: teamMgmtEnabled,
10870
- maxBudgetCeiling: teamAdminCfg.maxBudgetCeiling ?? null,
10886
+ maxBudgetCeiling: teamAdminCfg.maxBudgetCeiling,
10871
10887
  allowUnlimitedBudget: teamAdminCfg.allowUnlimitedBudget,
10872
10888
  objectPermissionsEnabled: objectPermsEnabled
10873
10889
  }
@@ -11263,8 +11279,8 @@ async function createRouter(options) {
11263
11279
  }
11264
11280
  const teams = await Promise.all(
11265
11281
  userInfo.teams.map(
11266
- (teamId) => client.getTeamInfo(teamId).catch((err) => {
11267
- logger.warn(`Failed to fetch team ${teamId}: ${err.message}`);
11282
+ (teamId) => withTeamFetchRetry(() => client.getTeamInfo(teamId)).catch((err) => {
11283
+ logger.warn(`Failed to fetch team ${teamId} after retries: ${err.message}`);
11268
11284
  return null;
11269
11285
  })
11270
11286
  )
@@ -11497,7 +11513,7 @@ async function createRouter(options) {
11497
11513
  }
11498
11514
  let existing;
11499
11515
  try {
11500
- existing = await client.getTeamInfo(teamId);
11516
+ existing = await withTeamFetchRetry(() => client.getTeamInfo(teamId));
11501
11517
  } catch (err) {
11502
11518
  if (err instanceof LiteLLMUpstreamError && err.status === 404) {
11503
11519
  res.status(404).json({ error: "Team not found" });
@@ -11611,7 +11627,7 @@ async function createRouter(options) {
11611
11627
  member: litellmUserId,
11612
11628
  owningGroup
11613
11629
  });
11614
- const updated = await client.getTeamInfo(teamId);
11630
+ const updated = await withTeamFetchRetry(() => client.getTeamInfo(teamId));
11615
11631
  res.json(updated);
11616
11632
  } catch (err) {
11617
11633
  sendTeamError(err, res);
@@ -11642,7 +11658,7 @@ async function createRouter(options) {
11642
11658
  member: litellmUserId,
11643
11659
  owningGroup
11644
11660
  });
11645
- const updated = await client.getTeamInfo(teamId);
11661
+ const updated = await withTeamFetchRetry(() => client.getTeamInfo(teamId));
11646
11662
  res.json(updated);
11647
11663
  } catch (err) {
11648
11664
  sendTeamError(err, res);