@acarmisc/backstage-plugin-litellm-backend 0.1.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 +6 -0
- package/dist/index.cjs.js +209 -0
- package/dist/index.cjs.js.map +7 -0
- package/dist/index.d.ts +4 -0
- package/dist/types.cjs.js +18 -0
- package/dist/types.cjs.js.map +7 -0
- package/dist/types.d.ts +74 -0
- package/package.json +43 -0
package/config.d.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/index.ts
|
|
20
|
+
var index_exports = {};
|
|
21
|
+
__export(index_exports, {
|
|
22
|
+
LiteLLMClient: () => LiteLLMClient,
|
|
23
|
+
createRouter: () => createRouter,
|
|
24
|
+
litellmPlugin: () => litellmPlugin
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/plugin.ts
|
|
29
|
+
var import_backend_plugin_api = require("@backstage/backend-plugin-api");
|
|
30
|
+
|
|
31
|
+
// src/router.ts
|
|
32
|
+
var import_express = require("express");
|
|
33
|
+
|
|
34
|
+
// src/client.ts
|
|
35
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
36
|
+
var LiteLLMClient = class {
|
|
37
|
+
constructor(config, timeout = DEFAULT_TIMEOUT) {
|
|
38
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
39
|
+
this.masterKey = config.masterKey;
|
|
40
|
+
this.timeout = timeout;
|
|
41
|
+
}
|
|
42
|
+
async request(path, options = {}) {
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
45
|
+
try {
|
|
46
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
47
|
+
...options,
|
|
48
|
+
signal: controller.signal,
|
|
49
|
+
headers: {
|
|
50
|
+
"Content-Type": "application/json",
|
|
51
|
+
"Authorization": `Bearer ${this.masterKey}`,
|
|
52
|
+
...options.headers
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
const errorBody = await response.text();
|
|
57
|
+
throw new Error(`LiteLLM API error: ${response.status} ${response.statusText} - ${errorBody}`);
|
|
58
|
+
}
|
|
59
|
+
return response.json();
|
|
60
|
+
} finally {
|
|
61
|
+
clearTimeout(timeoutId);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async getUserInfo(userId) {
|
|
65
|
+
const query = userId ? `?user_id=${encodeURIComponent(userId)}` : "";
|
|
66
|
+
return this.request(`/user/info${query}`);
|
|
67
|
+
}
|
|
68
|
+
async listKeys(userId) {
|
|
69
|
+
const query = userId ? `?user_id=${encodeURIComponent(userId)}` : "";
|
|
70
|
+
return this.request(`/key/info${query}`);
|
|
71
|
+
}
|
|
72
|
+
async generateKey(request) {
|
|
73
|
+
return this.request("/key/generate", {
|
|
74
|
+
method: "POST",
|
|
75
|
+
body: JSON.stringify(request)
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async deleteKeys(request) {
|
|
79
|
+
return this.request("/key/delete", {
|
|
80
|
+
method: "POST",
|
|
81
|
+
body: JSON.stringify(request)
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
async listModels() {
|
|
85
|
+
return this.request("/models");
|
|
86
|
+
}
|
|
87
|
+
async getUsage(startDate, endDate, userId, groupBy) {
|
|
88
|
+
const params = new URLSearchParams({
|
|
89
|
+
start_date: startDate,
|
|
90
|
+
end_date: endDate
|
|
91
|
+
});
|
|
92
|
+
if (userId) params.append("user_id", userId);
|
|
93
|
+
if (groupBy) params.append("group_by", groupBy);
|
|
94
|
+
return this.request(`/usage/keys?${params.toString()}`);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// src/router.ts
|
|
99
|
+
async function createRouter(options) {
|
|
100
|
+
const { config, logger } = options;
|
|
101
|
+
const baseUrl = config.getString("litellm.baseUrl");
|
|
102
|
+
const masterKey = config.getString("litellm.masterKey");
|
|
103
|
+
const client = new LiteLLMClient({ baseUrl, masterKey });
|
|
104
|
+
const router = (0, import_express.Router)();
|
|
105
|
+
router.get("/health", (_req, res) => {
|
|
106
|
+
res.json({ status: "ok" });
|
|
107
|
+
});
|
|
108
|
+
router.get("/user/info", async (req, res) => {
|
|
109
|
+
try {
|
|
110
|
+
const userId = req.query.user_id;
|
|
111
|
+
const userInfo = await client.getUserInfo(userId);
|
|
112
|
+
res.json(userInfo);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
logger.error("Failed to fetch user info", error);
|
|
115
|
+
res.status(500).json({ error: error.message });
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
router.get("/keys", async (req, res) => {
|
|
119
|
+
try {
|
|
120
|
+
const userId = req.query.user_id;
|
|
121
|
+
const keys = await client.listKeys(userId);
|
|
122
|
+
res.json(keys);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
logger.error("Failed to list keys", error);
|
|
125
|
+
res.status(500).json({ error: error.message });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
router.post("/keys/generate", async (req, res) => {
|
|
129
|
+
try {
|
|
130
|
+
const request = req.body;
|
|
131
|
+
const result = await client.generateKey(request);
|
|
132
|
+
res.json(result);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
logger.error("Failed to generate key", error);
|
|
135
|
+
res.status(500).json({ error: error.message });
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
router.delete("/keys/:keyId", async (req, res) => {
|
|
139
|
+
try {
|
|
140
|
+
const { keyId } = req.params;
|
|
141
|
+
if (!keyId) {
|
|
142
|
+
res.status(400).json({ error: "keyId is required" });
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
await client.deleteKeys({ keys: [keyId] });
|
|
146
|
+
res.json({ success: true });
|
|
147
|
+
} catch (error) {
|
|
148
|
+
logger.error("Failed to delete key", error);
|
|
149
|
+
res.status(500).json({ error: error.message });
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
router.get("/models", async (_req, res) => {
|
|
153
|
+
try {
|
|
154
|
+
const models = await client.listModels();
|
|
155
|
+
res.json(models);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
logger.error("Failed to list models", error);
|
|
158
|
+
res.status(500).json({ error: error.message });
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
router.get("/usage", async (req, res) => {
|
|
162
|
+
try {
|
|
163
|
+
const { start_date, end_date, user_id, group_by } = req.query;
|
|
164
|
+
if (!start_date || !end_date) {
|
|
165
|
+
res.status(400).json({ error: "start_date and end_date are required" });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const usage = await client.getUsage(
|
|
169
|
+
start_date,
|
|
170
|
+
end_date,
|
|
171
|
+
user_id,
|
|
172
|
+
group_by
|
|
173
|
+
);
|
|
174
|
+
res.json(usage);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
logger.error("Failed to fetch usage", error);
|
|
177
|
+
res.status(500).json({ error: error.message });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return router;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/plugin.ts
|
|
184
|
+
var litellmPlugin = (0, import_backend_plugin_api.createBackendPlugin)({
|
|
185
|
+
pluginId: "litellm",
|
|
186
|
+
register(reg) {
|
|
187
|
+
reg.registerInit({
|
|
188
|
+
deps: {
|
|
189
|
+
httpRouter: "coreServices.httpRouter",
|
|
190
|
+
config: "coreServices.rootConfig",
|
|
191
|
+
logger: "coreServices.logger"
|
|
192
|
+
},
|
|
193
|
+
async init({ httpRouter, config, logger }) {
|
|
194
|
+
const router = await createRouter({
|
|
195
|
+
config,
|
|
196
|
+
logger
|
|
197
|
+
});
|
|
198
|
+
httpRouter.use(router);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
204
|
+
0 && (module.exports = {
|
|
205
|
+
LiteLLMClient,
|
|
206
|
+
createRouter,
|
|
207
|
+
litellmPlugin
|
|
208
|
+
});
|
|
209
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/plugin.ts", "../src/router.ts", "../src/client.ts"],
|
|
4
|
+
"sourcesContent": ["export { litellmPlugin } from './plugin';\nexport { createRouter } from './router';\nexport * from './types';\nexport { LiteLLMClient } from './client';", "import { createBackendPlugin } from '@backstage/backend-plugin-api';\nimport { createRouter } from './router';\n\nexport const litellmPlugin = createBackendPlugin({\n pluginId: 'litellm',\n register(reg: any) {\n reg.registerInit({\n deps: {\n httpRouter: 'coreServices.httpRouter',\n config: 'coreServices.rootConfig',\n logger: 'coreServices.logger',\n },\n async init({ httpRouter, config, logger }) {\n const router = await createRouter({\n config,\n logger,\n });\n httpRouter.use(router);\n },\n });\n },\n});", "import { Router, Request, Response } from 'express';\nimport { Config } from '@backstage/config';\nimport { LiteLLMClient } from './client';\nimport {\n UserInfo,\n VirtualKey,\n ModelInfo,\n UsageMetrics,\n GenerateKeyRequest,\n GenerateKeyResponse,\n} from './types';\n\nexport interface RouterOptions {\n config: Config;\n logger: any;\n}\n\nexport async function createRouter(options: RouterOptions): Promise<Router> {\n const { config, logger } = options;\n\n const baseUrl = config.getString('litellm.baseUrl');\n const masterKey = config.getString('litellm.masterKey');\n const client = new LiteLLMClient({ baseUrl, masterKey });\n\n const router = Router();\n\n router.get('/health', (_req: Request, res: Response) => {\n res.json({ status: 'ok' });\n });\n\n router.get('/user/info', async (req: Request, res: Response) => {\n try {\n const userId = req.query.user_id as string | undefined;\n const userInfo: UserInfo = await client.getUserInfo(userId);\n res.json(userInfo);\n } catch (error: any) {\n logger.error('Failed to fetch user info', error);\n res.status(500).json({ error: error.message });\n }\n });\n\n router.get('/keys', async (req: Request, res: Response) => {\n try {\n const userId = req.query.user_id as string | undefined;\n const keys: VirtualKey[] = await client.listKeys(userId);\n res.json(keys);\n } catch (error: any) {\n logger.error('Failed to list keys', error);\n res.status(500).json({ error: error.message });\n }\n });\n\n router.post('/keys/generate', async (req: Request, res: Response) => {\n try {\n const request: GenerateKeyRequest = req.body;\n const result: GenerateKeyResponse = await client.generateKey(request);\n res.json(result);\n } catch (error: any) {\n logger.error('Failed to generate key', error);\n res.status(500).json({ error: error.message });\n }\n });\n\n router.delete('/keys/:keyId', async (req: Request, res: Response) => {\n try {\n const { keyId } = req.params;\n if (!keyId) {\n res.status(400).json({ error: 'keyId is required' });\n return;\n }\n await client.deleteKeys({ keys: [keyId] });\n res.json({ success: true });\n } catch (error: any) {\n logger.error('Failed to delete key', error);\n res.status(500).json({ error: error.message });\n }\n });\n\n router.get('/models', async (_req: Request, res: Response) => {\n try {\n const models: ModelInfo[] = await client.listModels();\n res.json(models);\n } catch (error: any) {\n logger.error('Failed to list models', error);\n res.status(500).json({ error: error.message });\n }\n });\n\n router.get('/usage', async (req: Request, res: Response) => {\n try {\n const { start_date, end_date, user_id, group_by } = req.query;\n if (!start_date || !end_date) {\n res.status(400).json({ error: 'start_date and end_date are required' });\n return;\n }\n const usage: UsageMetrics = await client.getUsage(\n start_date as string,\n end_date as string,\n user_id as string | undefined,\n group_by as string | undefined\n );\n res.json(usage);\n } catch (error: any) {\n logger.error('Failed to fetch usage', error);\n res.status(500).json({ error: error.message });\n }\n });\n\n return router;\n}", "import { LiteLLMConfig, UserInfo, VirtualKey, ModelInfo, UsageMetrics, GenerateKeyRequest, GenerateKeyResponse, DeleteKeyRequest } from './types';\n\nconst DEFAULT_TIMEOUT = 30000;\n\nexport class LiteLLMClient {\n private baseUrl: string;\n private masterKey: string;\n private timeout: number;\n\n constructor(config: LiteLLMConfig, timeout = DEFAULT_TIMEOUT) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '');\n this.masterKey = config.masterKey;\n this.timeout = timeout;\n }\n\n private async request<T>(path: string, options: RequestInit = {}): Promise<T> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(`${this.baseUrl}${path}`, {\n ...options,\n signal: controller.signal,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.masterKey}`,\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`LiteLLM API error: ${response.status} ${response.statusText} - ${errorBody}`);\n }\n\n return response.json();\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async getUserInfo(userId?: string): Promise<UserInfo> {\n const query = userId ? `?user_id=${encodeURIComponent(userId)}` : '';\n return this.request<UserInfo>(`/user/info${query}`);\n }\n\n async listKeys(userId?: string): Promise<VirtualKey[]> {\n const query = userId ? `?user_id=${encodeURIComponent(userId)}` : '';\n return this.request<VirtualKey[]>(`/key/info${query}`);\n }\n\n async generateKey(request: GenerateKeyRequest): Promise<GenerateKeyResponse> {\n return this.request<GenerateKeyResponse>('/key/generate', {\n method: 'POST',\n body: JSON.stringify(request),\n });\n }\n\n async deleteKeys(request: DeleteKeyRequest): Promise<{ success: boolean }> {\n return this.request<{ success: boolean }>('/key/delete', {\n method: 'POST',\n body: JSON.stringify(request),\n });\n }\n\n async listModels(): Promise<ModelInfo[]> {\n return this.request<ModelInfo[]>('/models');\n }\n\n async getUsage(startDate: string, endDate: string, userId?: string, groupBy?: string): Promise<UsageMetrics> {\n const params = new URLSearchParams({\n start_date: startDate,\n end_date: endDate,\n });\n if (userId) params.append('user_id', userId);\n if (groupBy) params.append('group_by', groupBy);\n\n return this.request<UsageMetrics>(`/usage/keys?${params.toString()}`);\n }\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __copyProps = (to, from, except, desc) => {
|
|
6
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
7
|
+
for (let key of __getOwnPropNames(from))
|
|
8
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
9
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
10
|
+
}
|
|
11
|
+
return to;
|
|
12
|
+
};
|
|
13
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
14
|
+
|
|
15
|
+
// src/types.ts
|
|
16
|
+
var types_exports = {};
|
|
17
|
+
module.exports = __toCommonJS(types_exports);
|
|
18
|
+
//# sourceMappingURL=types.cjs.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/types.ts"],
|
|
4
|
+
"sourcesContent": ["export interface UserInfo {\n user_id: string;\n email: string;\n team_id?: string;\n team_alias?: string;\n max_budget?: number;\n current_spend?: number;\n soft_limit?: number;\n hard_limit?: number;\n}\n\nexport interface VirtualKey {\n key: string;\n key_alias?: string;\n created_at: string;\n expires_at?: string;\n spend: number;\n max_budget?: number;\n tpm_limit?: number;\n rpm_limit?: number;\n models?: string[];\n user_id?: string;\n}\n\nexport interface ModelInfo {\n model_name: string;\n mode: string;\n supports_function_calling?: boolean;\n supports_vision?: boolean;\n input_cost_per_token?: number;\n output_cost_per_token?: number;\n}\n\nexport interface UsageMetrics {\n total_spend: number;\n total_tokens: number;\n prompt_tokens: number;\n completion_tokens: number;\n usage_by_model: Record<string, {\n total_spend: number;\n total_tokens: number;\n prompt_tokens: number;\n completion_tokens: number;\n }>;\n daily_usage: Array<{\n date: string;\n spend: number;\n total_tokens: number;\n prompt_tokens: number;\n completion_tokens: number;\n }>;\n}\n\nexport interface GenerateKeyRequest {\n alias?: string;\n models?: string[];\n duration?: string;\n max_budget?: number;\n tpm_limit?: number;\n rpm_limit?: number;\n user_id?: string;\n}\n\nexport interface GenerateKeyResponse {\n key: string;\n key_alias?: string;\n expires_at?: string;\n max_budget?: number;\n tpm_limit?: number;\n rpm_limit?: number;\n models?: string[];\n}\n\nexport interface DeleteKeyRequest {\n keys: string[];\n}\n\nexport interface LiteLLMConfig {\n baseUrl: string;\n masterKey: string;\n}"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export interface UserInfo {
|
|
2
|
+
user_id: string;
|
|
3
|
+
email: string;
|
|
4
|
+
team_id?: string;
|
|
5
|
+
team_alias?: string;
|
|
6
|
+
max_budget?: number;
|
|
7
|
+
current_spend?: number;
|
|
8
|
+
soft_limit?: number;
|
|
9
|
+
hard_limit?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface VirtualKey {
|
|
12
|
+
key: string;
|
|
13
|
+
key_alias?: string;
|
|
14
|
+
created_at: string;
|
|
15
|
+
expires_at?: string;
|
|
16
|
+
spend: number;
|
|
17
|
+
max_budget?: number;
|
|
18
|
+
tpm_limit?: number;
|
|
19
|
+
rpm_limit?: number;
|
|
20
|
+
models?: string[];
|
|
21
|
+
user_id?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ModelInfo {
|
|
24
|
+
model_name: string;
|
|
25
|
+
mode: string;
|
|
26
|
+
supports_function_calling?: boolean;
|
|
27
|
+
supports_vision?: boolean;
|
|
28
|
+
input_cost_per_token?: number;
|
|
29
|
+
output_cost_per_token?: number;
|
|
30
|
+
}
|
|
31
|
+
export interface UsageMetrics {
|
|
32
|
+
total_spend: number;
|
|
33
|
+
total_tokens: number;
|
|
34
|
+
prompt_tokens: number;
|
|
35
|
+
completion_tokens: number;
|
|
36
|
+
usage_by_model: Record<string, {
|
|
37
|
+
total_spend: number;
|
|
38
|
+
total_tokens: number;
|
|
39
|
+
prompt_tokens: number;
|
|
40
|
+
completion_tokens: number;
|
|
41
|
+
}>;
|
|
42
|
+
daily_usage: Array<{
|
|
43
|
+
date: string;
|
|
44
|
+
spend: number;
|
|
45
|
+
total_tokens: number;
|
|
46
|
+
prompt_tokens: number;
|
|
47
|
+
completion_tokens: number;
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
50
|
+
export interface GenerateKeyRequest {
|
|
51
|
+
alias?: string;
|
|
52
|
+
models?: string[];
|
|
53
|
+
duration?: string;
|
|
54
|
+
max_budget?: number;
|
|
55
|
+
tpm_limit?: number;
|
|
56
|
+
rpm_limit?: number;
|
|
57
|
+
user_id?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface GenerateKeyResponse {
|
|
60
|
+
key: string;
|
|
61
|
+
key_alias?: string;
|
|
62
|
+
expires_at?: string;
|
|
63
|
+
max_budget?: number;
|
|
64
|
+
tpm_limit?: number;
|
|
65
|
+
rpm_limit?: number;
|
|
66
|
+
models?: string[];
|
|
67
|
+
}
|
|
68
|
+
export interface DeleteKeyRequest {
|
|
69
|
+
keys: string[];
|
|
70
|
+
}
|
|
71
|
+
export interface LiteLLMConfig {
|
|
72
|
+
baseUrl: string;
|
|
73
|
+
masterKey: string;
|
|
74
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@acarmisc/backstage-plugin-litellm-backend",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The Backstage backend plugin for LiteLLM governance",
|
|
5
|
+
"backstage": {
|
|
6
|
+
"role": "backend-plugin",
|
|
7
|
+
"pluginId": "litellm",
|
|
8
|
+
"pluginPackages": [
|
|
9
|
+
"@govai/backstage-plugin-litellm-backend"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"license": "Apache-2.0",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./dist/index.cjs.js"
|
|
18
|
+
},
|
|
19
|
+
"main": "dist/index.cjs.js",
|
|
20
|
+
"types": "dist/index.d.ts",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"config.d.ts"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "echo 'Build dist/ from Backstage monorepo: backstage-cli package build'",
|
|
27
|
+
"prepack": "echo 'Dist files must be built in Backstage monorepo and copied here'",
|
|
28
|
+
"postpack": ""
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@backstage/backend-plugin-api": "^1.0.1",
|
|
32
|
+
"@backstage/config": "^1.2.0",
|
|
33
|
+
"@backstage/types": "^1.1.0",
|
|
34
|
+
"express": "^4.18.2"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@backstage/cli": "^0.28.0",
|
|
38
|
+
"@types/express": "^4.17.25",
|
|
39
|
+
"esbuild": "^0.28.0",
|
|
40
|
+
"typescript": "^5.9.3"
|
|
41
|
+
},
|
|
42
|
+
"configSchema": "config.d.ts"
|
|
43
|
+
}
|