@acarmisc/backstage-plugin-litellm-backend 0.11.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/dist/index.cjs.js +24 -5
- package/dist/index.cjs.js.map +2 -2
- package/dist/router.js +43 -5
- package/package.json +1 -1
package/dist/router.js
CHANGED
|
@@ -46,6 +46,44 @@ const bridge_1 = require("./bridge");
|
|
|
46
46
|
const permissions_1 = require("./permissions");
|
|
47
47
|
const teamAdmin_1 = require("./teamAdmin");
|
|
48
48
|
const teamCreateInFlight = new Map();
|
|
49
|
+
/** Backoff schedule for `withTeamFetchRetry` — 3 retries over ~1.3s total. */
|
|
50
|
+
const TEAM_FETCH_RETRY_DELAYS_MS = [100, 300, 900];
|
|
51
|
+
/**
|
|
52
|
+
* Only 5xx (and network/timeout) failures are treated as transient — a 4xx
|
|
53
|
+
* like "team not found" or "forbidden" is a deterministic answer from the
|
|
54
|
+
* upstream and retrying it would just add latency for the same result.
|
|
55
|
+
*/
|
|
56
|
+
function isRetryableTeamFetchError(err) {
|
|
57
|
+
if (err instanceof client_1.LiteLLMUpstreamError) {
|
|
58
|
+
return err.status >= 500;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Retries a LiteLLM team-info fetch with the given backoff schedule.
|
|
64
|
+
*
|
|
65
|
+
* LiteLLM proxies are commonly run behind multiple replicas; a request can
|
|
66
|
+
* land on a replica that hasn't yet caught up with a very recent write
|
|
67
|
+
* (team create/update, membership change), or on one that is transiently
|
|
68
|
+
* unhealthy. Both surface as a failed `getTeamInfo` call that would
|
|
69
|
+
* otherwise be swallowed (e.g. GET /teams silently drops the team from the
|
|
70
|
+
* response) or bubble up as a spurious 500/404 right after a write this
|
|
71
|
+
* same request just made. A short retry smooths over that window without
|
|
72
|
+
* masking a genuine, persistent failure.
|
|
73
|
+
*/
|
|
74
|
+
async function withTeamFetchRetry(fn, delaysMs = TEAM_FETCH_RETRY_DELAYS_MS) {
|
|
75
|
+
for (let attempt = 0;; attempt++) {
|
|
76
|
+
try {
|
|
77
|
+
return await fn();
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
if (attempt >= delaysMs.length || !isRetryableTeamFetchError(err)) {
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
await new Promise(resolve => setTimeout(resolve, delaysMs[attempt]));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
49
87
|
async function createRouter(options) {
|
|
50
88
|
const { config, logger, auth, discovery, permissions } = options;
|
|
51
89
|
const baseUrl = config.getString('litellm.baseUrl');
|
|
@@ -510,8 +548,8 @@ async function createRouter(options) {
|
|
|
510
548
|
res.json([]);
|
|
511
549
|
return;
|
|
512
550
|
}
|
|
513
|
-
const teams = await Promise.all(userInfo.teams.map(teamId => client.getTeamInfo(teamId).catch(err => {
|
|
514
|
-
logger.warn(`Failed to fetch team ${teamId}: ${err.message}`);
|
|
551
|
+
const teams = await Promise.all(userInfo.teams.map(teamId => withTeamFetchRetry(() => client.getTeamInfo(teamId)).catch(err => {
|
|
552
|
+
logger.warn(`Failed to fetch team ${teamId} after retries: ${err.message}`);
|
|
515
553
|
return null;
|
|
516
554
|
})));
|
|
517
555
|
res.json(teams.filter(Boolean));
|
|
@@ -773,7 +811,7 @@ async function createRouter(options) {
|
|
|
773
811
|
}
|
|
774
812
|
let existing;
|
|
775
813
|
try {
|
|
776
|
-
existing = await client.getTeamInfo(teamId);
|
|
814
|
+
existing = await withTeamFetchRetry(() => client.getTeamInfo(teamId));
|
|
777
815
|
}
|
|
778
816
|
catch (err) {
|
|
779
817
|
if (err instanceof client_1.LiteLLMUpstreamError && err.status === 404) {
|
|
@@ -885,7 +923,7 @@ async function createRouter(options) {
|
|
|
885
923
|
member: litellmUserId,
|
|
886
924
|
owningGroup,
|
|
887
925
|
});
|
|
888
|
-
const updated = await client.getTeamInfo(teamId);
|
|
926
|
+
const updated = await withTeamFetchRetry(() => client.getTeamInfo(teamId));
|
|
889
927
|
res.json(updated);
|
|
890
928
|
}
|
|
891
929
|
catch (err) {
|
|
@@ -920,7 +958,7 @@ async function createRouter(options) {
|
|
|
920
958
|
member: litellmUserId,
|
|
921
959
|
owningGroup,
|
|
922
960
|
});
|
|
923
|
-
const updated = await client.getTeamInfo(teamId);
|
|
961
|
+
const updated = await withTeamFetchRetry(() => client.getTeamInfo(teamId));
|
|
924
962
|
res.json(updated);
|
|
925
963
|
}
|
|
926
964
|
catch (err) {
|