@acarmisc/backstage-plugin-litellm-backend 0.1.7 → 0.1.10

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/dist/index.cjs.js CHANGED
@@ -5658,6 +5658,12 @@ var LiteLLMClient = class {
5658
5658
  body: JSON.stringify({ json: request })
5659
5659
  });
5660
5660
  }
5661
+ async updateKey(request) {
5662
+ return this.request("/key/update", {
5663
+ method: "POST",
5664
+ body: JSON.stringify(request)
5665
+ });
5666
+ }
5661
5667
  async deleteKeys(request) {
5662
5668
  return this.request("/key/delete", {
5663
5669
  method: "POST",
@@ -5671,22 +5677,90 @@ var LiteLLMClient = class {
5671
5677
  async getTeamInfo(teamId) {
5672
5678
  return this.request(`/team/info?team_id=${encodeURIComponent(teamId)}`);
5673
5679
  }
5674
- async getUsage(startDate, endDate, userId, groupBy) {
5675
- const params = new URLSearchParams({ start_date: startDate, end_date: endDate });
5680
+ emptyUsage() {
5681
+ return {
5682
+ total_spend: 0,
5683
+ total_tokens: 0,
5684
+ prompt_tokens: 0,
5685
+ completion_tokens: 0,
5686
+ usage_by_model: {},
5687
+ daily_usage: []
5688
+ };
5689
+ }
5690
+ /**
5691
+ * Transforms LiteLLM's SpendAnalyticsPaginatedResponse into the flatter
5692
+ * UsageMetrics shape consumed by the frontend charts.
5693
+ */
5694
+ transformDailyActivity(response) {
5695
+ const results = Array.isArray(response?.results) ? response.results : [];
5696
+ const meta = response?.metadata ?? {};
5697
+ const daily_usage = results.map((r) => ({
5698
+ date: r.date,
5699
+ spend: r.metrics?.spend ?? 0,
5700
+ total_tokens: r.metrics?.total_tokens ?? 0,
5701
+ prompt_tokens: r.metrics?.prompt_tokens ?? 0,
5702
+ completion_tokens: r.metrics?.completion_tokens ?? 0
5703
+ })).sort((a, b) => a.date.localeCompare(b.date));
5704
+ const usage_by_model = {};
5705
+ for (const r of results) {
5706
+ const models = r.breakdown?.models ?? {};
5707
+ for (const [name, entry] of Object.entries(models)) {
5708
+ const m = entry?.metrics ?? {};
5709
+ const bucket = usage_by_model[name] ?? {
5710
+ total_spend: 0,
5711
+ total_tokens: 0,
5712
+ prompt_tokens: 0,
5713
+ completion_tokens: 0
5714
+ };
5715
+ bucket.total_spend += m.spend ?? 0;
5716
+ bucket.total_tokens += m.total_tokens ?? 0;
5717
+ bucket.prompt_tokens += m.prompt_tokens ?? 0;
5718
+ bucket.completion_tokens += m.completion_tokens ?? 0;
5719
+ usage_by_model[name] = bucket;
5720
+ }
5721
+ }
5722
+ return {
5723
+ total_spend: meta.total_spend ?? 0,
5724
+ total_tokens: meta.total_tokens ?? 0,
5725
+ prompt_tokens: meta.total_prompt_tokens ?? 0,
5726
+ completion_tokens: meta.total_completion_tokens ?? 0,
5727
+ usage_by_model,
5728
+ daily_usage
5729
+ };
5730
+ }
5731
+ async getUsage(startDate, endDate, userId, _groupBy) {
5732
+ const params = new URLSearchParams({
5733
+ start_date: startDate,
5734
+ end_date: endDate,
5735
+ page_size: "100"
5736
+ });
5676
5737
  if (userId) params.append("user_id", userId);
5677
- if (groupBy) params.append("group_by", groupBy);
5678
5738
  try {
5679
- return await this.request(`/usage/keys?${params.toString()}`);
5739
+ const response = await this.request(`/user/daily/activity?${params.toString()}`);
5740
+ return this.transformDailyActivity(response);
5680
5741
  } catch (err) {
5681
5742
  if (err.status === 404 || err.message.includes("not found")) {
5682
- return { total_spend: 0, total_tokens: 0, prompt_tokens: 0, completion_tokens: 0, usage_by_model: {}, daily_usage: [] };
5743
+ return this.emptyUsage();
5683
5744
  }
5684
5745
  throw err;
5685
5746
  }
5686
5747
  }
5687
5748
  async getTeamUsage(teamId, startDate, endDate) {
5688
- const params = new URLSearchParams({ start_date: startDate, end_date: endDate, team_id: teamId });
5689
- return this.request(`/usage/keys?${params.toString()}`);
5749
+ const params = new URLSearchParams({
5750
+ start_date: startDate,
5751
+ end_date: endDate,
5752
+ team_ids: teamId,
5753
+ page_size: "100"
5754
+ });
5755
+ try {
5756
+ const response = await this.request(`/team/daily/activity?${params.toString()}`);
5757
+ return this.transformDailyActivity(response);
5758
+ } catch (err) {
5759
+ if (err.status === 404 || err.message.includes("not found")) {
5760
+ return this.emptyUsage();
5761
+ }
5762
+ throw err;
5763
+ }
5690
5764
  }
5691
5765
  };
5692
5766
 
@@ -5860,6 +5934,21 @@ async function createRouter(options) {
5860
5934
  res.status(500).json({ error: error.message });
5861
5935
  }
5862
5936
  });
5937
+ router.post("/keys/:keyId/update", async (req, res) => {
5938
+ try {
5939
+ const { keyId } = req.params;
5940
+ if (!keyId) {
5941
+ res.status(400).json({ error: "keyId is required" });
5942
+ return;
5943
+ }
5944
+ const request = { ...req.body, key: keyId };
5945
+ const result = await client.updateKey(request);
5946
+ res.json(result);
5947
+ } catch (error) {
5948
+ logger.error("Failed to update key", error);
5949
+ res.status(500).json({ error: error.message });
5950
+ }
5951
+ });
5863
5952
  router.delete("/keys/:keyId", async (req, res) => {
5864
5953
  try {
5865
5954
  const { keyId } = req.params;