@acarmisc/backstage-plugin-litellm-backend 0.6.2 → 0.8.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 +21 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.js +31 -4
- package/dist/index.cjs.js +42 -8
- package/dist/index.cjs.js.map +3 -3
- package/dist/openapi.js +6 -2
- package/dist/router.js +16 -1
- package/package.json +1 -1
package/config.d.ts
CHANGED
|
@@ -127,6 +127,27 @@ export interface Config {
|
|
|
127
127
|
group?: string;
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Controls for the "Generate New Key" form in the frontend.
|
|
132
|
+
*/
|
|
133
|
+
keyGeneration?: {
|
|
134
|
+
/**
|
|
135
|
+
* When true, users can tick an "Unlimited budget" checkbox that skips
|
|
136
|
+
* the max-budget cap entirely. The checkbox is hidden from the form
|
|
137
|
+
* when this is false, so a budget is always required.
|
|
138
|
+
* @default false
|
|
139
|
+
*/
|
|
140
|
+
allowUnlimitedBudget?: boolean;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* When true (the default), a team must be selected before a key can
|
|
144
|
+
* be generated. Set to false to let users generate personal,
|
|
145
|
+
* team-less keys.
|
|
146
|
+
* @default true
|
|
147
|
+
*/
|
|
148
|
+
teamRequired?: boolean;
|
|
149
|
+
};
|
|
150
|
+
|
|
130
151
|
bridge?: {
|
|
131
152
|
/**
|
|
132
153
|
* When true, mount the /bridge/keys, /bridge/keys (POST), /bridge/models
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { LiteLLMConfig, UserInfo, VirtualKey, ModelInfo, UsageMetrics, TeamInfo, GenerateKeyRequest, GenerateKeyResponse, UpdateKeyRequest, DeleteKeyRequest, CreateUserRequest, CreateUserResponse, AuditLogsParams, PaginatedAuditLogs } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Typed error for failed upstream LiteLLM responses. Preserves the HTTP
|
|
4
|
+
* status and the structured `param` (e.g. `key_alias`) from the upstream
|
|
5
|
+
* body so the router can surface a 400 instead of collapsing everything
|
|
6
|
+
* into a 500. The message is the upstream `error.message` when present,
|
|
7
|
+
* otherwise the raw body text.
|
|
8
|
+
*/
|
|
9
|
+
export declare class LiteLLMUpstreamError extends Error {
|
|
10
|
+
status: number;
|
|
11
|
+
param?: string;
|
|
12
|
+
constructor(status: number, statusText: string, body: string);
|
|
13
|
+
}
|
|
2
14
|
export declare class LiteLLMClient {
|
|
3
15
|
private baseUrl;
|
|
4
16
|
private masterKey;
|
package/dist/client.js
CHANGED
|
@@ -1,7 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LiteLLMClient = void 0;
|
|
3
|
+
exports.LiteLLMClient = exports.LiteLLMUpstreamError = void 0;
|
|
4
4
|
const DEFAULT_TIMEOUT = 30000;
|
|
5
|
+
/**
|
|
6
|
+
* Typed error for failed upstream LiteLLM responses. Preserves the HTTP
|
|
7
|
+
* status and the structured `param` (e.g. `key_alias`) from the upstream
|
|
8
|
+
* body so the router can surface a 400 instead of collapsing everything
|
|
9
|
+
* into a 500. The message is the upstream `error.message` when present,
|
|
10
|
+
* otherwise the raw body text.
|
|
11
|
+
*/
|
|
12
|
+
class LiteLLMUpstreamError extends Error {
|
|
13
|
+
constructor(status, statusText, body) {
|
|
14
|
+
let message = `LiteLLM API error: ${status} ${statusText} - ${body}`;
|
|
15
|
+
let param;
|
|
16
|
+
try {
|
|
17
|
+
const parsed = JSON.parse(body);
|
|
18
|
+
const inner = parsed?.error ?? parsed;
|
|
19
|
+
if (typeof inner?.message === 'string')
|
|
20
|
+
message = inner.message;
|
|
21
|
+
if (typeof inner?.param === 'string' && inner.param !== 'None') {
|
|
22
|
+
param = inner.param;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// not JSON — keep the raw body in the message
|
|
27
|
+
}
|
|
28
|
+
super(message);
|
|
29
|
+
this.status = status;
|
|
30
|
+
this.param = param;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.LiteLLMUpstreamError = LiteLLMUpstreamError;
|
|
5
34
|
class LiteLLMClient {
|
|
6
35
|
constructor(config, timeout = DEFAULT_TIMEOUT) {
|
|
7
36
|
this.baseUrl = config.baseUrl.replace(/\/$/, '');
|
|
@@ -28,9 +57,7 @@ class LiteLLMClient {
|
|
|
28
57
|
});
|
|
29
58
|
if (!response.ok) {
|
|
30
59
|
const errorBody = await response.text();
|
|
31
|
-
|
|
32
|
-
err.status = response.status;
|
|
33
|
-
throw err;
|
|
60
|
+
throw new LiteLLMUpstreamError(response.status, response.statusText, errorBody);
|
|
34
61
|
}
|
|
35
62
|
return response.json();
|
|
36
63
|
}
|
package/dist/index.cjs.js
CHANGED
|
@@ -63,6 +63,24 @@ var import_catalog_client = require("@backstage/catalog-client");
|
|
|
63
63
|
|
|
64
64
|
// src/client.ts
|
|
65
65
|
var DEFAULT_TIMEOUT = 3e4;
|
|
66
|
+
var LiteLLMUpstreamError = class extends Error {
|
|
67
|
+
constructor(status, statusText, body) {
|
|
68
|
+
let message2 = `LiteLLM API error: ${status} ${statusText} - ${body}`;
|
|
69
|
+
let param;
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(body);
|
|
72
|
+
const inner = parsed?.error ?? parsed;
|
|
73
|
+
if (typeof inner?.message === "string") message2 = inner.message;
|
|
74
|
+
if (typeof inner?.param === "string" && inner.param !== "None") {
|
|
75
|
+
param = inner.param;
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
super(message2);
|
|
80
|
+
this.status = status;
|
|
81
|
+
this.param = param;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
66
84
|
var LiteLLMClient = class {
|
|
67
85
|
constructor(config, timeout = DEFAULT_TIMEOUT) {
|
|
68
86
|
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
@@ -84,11 +102,11 @@ var LiteLLMClient = class {
|
|
|
84
102
|
});
|
|
85
103
|
if (!response.ok) {
|
|
86
104
|
const errorBody = await response.text();
|
|
87
|
-
|
|
88
|
-
|
|
105
|
+
throw new LiteLLMUpstreamError(
|
|
106
|
+
response.status,
|
|
107
|
+
response.statusText,
|
|
108
|
+
errorBody
|
|
89
109
|
);
|
|
90
|
-
err.status = response.status;
|
|
91
|
-
throw err;
|
|
92
110
|
}
|
|
93
111
|
return response.json();
|
|
94
112
|
} finally {
|
|
@@ -511,12 +529,16 @@ var openApiSpec = {
|
|
|
511
529
|
"/config": {
|
|
512
530
|
get: {
|
|
513
531
|
tags: ["System"],
|
|
514
|
-
summary: "Public LiteLLM proxy base URL
|
|
532
|
+
summary: "Public LiteLLM proxy base URL and key-generation form settings",
|
|
515
533
|
responses: {
|
|
516
534
|
"200": {
|
|
517
|
-
description: "
|
|
535
|
+
description: "Frontend config",
|
|
518
536
|
content: { "application/json": { schema: { type: "object", properties: {
|
|
519
|
-
baseUrl: { type: "string" }
|
|
537
|
+
baseUrl: { type: "string" },
|
|
538
|
+
keyGeneration: { type: "object", properties: {
|
|
539
|
+
allowUnlimitedBudget: { type: "boolean" },
|
|
540
|
+
teamRequired: { type: "boolean" }
|
|
541
|
+
} }
|
|
520
542
|
} } } }
|
|
521
543
|
}
|
|
522
544
|
}
|
|
@@ -2465,6 +2487,8 @@ async function createRouter(options) {
|
|
|
2465
2487
|
const { enabled: provisioningEnabled, defaults: provisioningDefaults } = readProvisioningDefaults(config);
|
|
2466
2488
|
const roleConfigs = readRoleConfigs(config);
|
|
2467
2489
|
const auditGroup = config.getOptionalString("litellm.audit.group");
|
|
2490
|
+
const allowUnlimitedBudget = config.getOptionalBoolean("litellm.keyGeneration.allowUnlimitedBudget") ?? false;
|
|
2491
|
+
const teamRequired = config.getOptionalBoolean("litellm.keyGeneration.teamRequired") ?? true;
|
|
2468
2492
|
const catalogClient = new import_catalog_client.CatalogClient({ discoveryApi: discovery });
|
|
2469
2493
|
if (provisioningEnabled) {
|
|
2470
2494
|
logger.info(
|
|
@@ -2477,7 +2501,10 @@ async function createRouter(options) {
|
|
|
2477
2501
|
res.json({ status: "ok", provisioning: provisioningEnabled });
|
|
2478
2502
|
});
|
|
2479
2503
|
router.get("/config", (_req, res) => {
|
|
2480
|
-
res.json({
|
|
2504
|
+
res.json({
|
|
2505
|
+
baseUrl: publicBaseUrl,
|
|
2506
|
+
keyGeneration: { allowUnlimitedBudget, teamRequired }
|
|
2507
|
+
});
|
|
2481
2508
|
});
|
|
2482
2509
|
router.get("/openapi.json", (_req, res) => {
|
|
2483
2510
|
res.json(openApiSpec);
|
|
@@ -2669,6 +2696,13 @@ async function createRouter(options) {
|
|
|
2669
2696
|
});
|
|
2670
2697
|
return;
|
|
2671
2698
|
}
|
|
2699
|
+
if (error instanceof LiteLLMUpstreamError) {
|
|
2700
|
+
res.status(error.status).json({
|
|
2701
|
+
error: error.message,
|
|
2702
|
+
...error.param ? { param: error.param } : {}
|
|
2703
|
+
});
|
|
2704
|
+
return;
|
|
2705
|
+
}
|
|
2672
2706
|
logger.error("Failed to generate key", error);
|
|
2673
2707
|
res.status(500).json({ error: error.message });
|
|
2674
2708
|
}
|