@acarmisc/backstage-plugin-litellm-backend 0.1.0 → 0.1.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/client.d.ts +16 -0
- package/dist/client.js +71 -0
- package/dist/index.cjs.js +11 -9
- package/dist/index.cjs.js.map +2 -2
- package/dist/index.js +24 -0
- package/dist/plugin.d.ts +1 -0
- package/dist/plugin.js +23 -0
- package/dist/router.d.ts +7 -0
- package/dist/router.js +89 -0
- package/dist/types.cjs.js +1 -0
- package/dist/types.cjs.js.map +1 -1
- package/dist/types.js +2 -0
- package/package.json +2 -2
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { LiteLLMConfig, UserInfo, VirtualKey, ModelInfo, UsageMetrics, GenerateKeyRequest, GenerateKeyResponse, DeleteKeyRequest } from './types';
|
|
2
|
+
export declare class LiteLLMClient {
|
|
3
|
+
private baseUrl;
|
|
4
|
+
private masterKey;
|
|
5
|
+
private timeout;
|
|
6
|
+
constructor(config: LiteLLMConfig, timeout?: number);
|
|
7
|
+
private request;
|
|
8
|
+
getUserInfo(userId?: string): Promise<UserInfo>;
|
|
9
|
+
listKeys(userId?: string): Promise<VirtualKey[]>;
|
|
10
|
+
generateKey(request: GenerateKeyRequest): Promise<GenerateKeyResponse>;
|
|
11
|
+
deleteKeys(request: DeleteKeyRequest): Promise<{
|
|
12
|
+
success: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
listModels(): Promise<ModelInfo[]>;
|
|
15
|
+
getUsage(startDate: string, endDate: string, userId?: string, groupBy?: string): Promise<UsageMetrics>;
|
|
16
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LiteLLMClient = void 0;
|
|
4
|
+
const DEFAULT_TIMEOUT = 30000;
|
|
5
|
+
class LiteLLMClient {
|
|
6
|
+
constructor(config, timeout = DEFAULT_TIMEOUT) {
|
|
7
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, '');
|
|
8
|
+
this.masterKey = config.masterKey;
|
|
9
|
+
this.timeout = timeout;
|
|
10
|
+
}
|
|
11
|
+
async request(path, options = {}) {
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
16
|
+
...options,
|
|
17
|
+
signal: controller.signal,
|
|
18
|
+
headers: {
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
'Authorization': `Bearer ${this.masterKey}`,
|
|
21
|
+
...options.headers,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
const errorBody = await response.text();
|
|
26
|
+
throw new Error(`LiteLLM API error: ${response.status} ${response.statusText} - ${errorBody}`);
|
|
27
|
+
}
|
|
28
|
+
return response.json();
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
clearTimeout(timeoutId);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async getUserInfo(userId) {
|
|
35
|
+
const query = userId ? `?user_id=${encodeURIComponent(userId)}` : '';
|
|
36
|
+
return this.request(`/user/info${query}`);
|
|
37
|
+
}
|
|
38
|
+
async listKeys(userId) {
|
|
39
|
+
const query = userId ? `?user_id=${encodeURIComponent(userId)}` : '';
|
|
40
|
+
const response = await this.request(`/key/info${query}`);
|
|
41
|
+
return Array.isArray(response) ? response : (response.info ?? []);
|
|
42
|
+
}
|
|
43
|
+
async generateKey(request) {
|
|
44
|
+
return this.request('/key/generate', {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
body: JSON.stringify(request),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async deleteKeys(request) {
|
|
50
|
+
return this.request('/key/delete', {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
body: JSON.stringify(request),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
async listModels() {
|
|
56
|
+
const response = await this.request('/models');
|
|
57
|
+
return Array.isArray(response) ? response : (response.data ?? []);
|
|
58
|
+
}
|
|
59
|
+
async getUsage(startDate, endDate, userId, groupBy) {
|
|
60
|
+
const params = new URLSearchParams({
|
|
61
|
+
start_date: startDate,
|
|
62
|
+
end_date: endDate,
|
|
63
|
+
});
|
|
64
|
+
if (userId)
|
|
65
|
+
params.append('user_id', userId);
|
|
66
|
+
if (groupBy)
|
|
67
|
+
params.append('group_by', groupBy);
|
|
68
|
+
return this.request(`/usage/keys?${params.toString()}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.LiteLLMClient = LiteLLMClient;
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -67,7 +68,8 @@ var LiteLLMClient = class {
|
|
|
67
68
|
}
|
|
68
69
|
async listKeys(userId) {
|
|
69
70
|
const query = userId ? `?user_id=${encodeURIComponent(userId)}` : "";
|
|
70
|
-
|
|
71
|
+
const response = await this.request(`/key/info${query}`);
|
|
72
|
+
return Array.isArray(response) ? response : response.info ?? [];
|
|
71
73
|
}
|
|
72
74
|
async generateKey(request) {
|
|
73
75
|
return this.request("/key/generate", {
|
|
@@ -82,7 +84,8 @@ var LiteLLMClient = class {
|
|
|
82
84
|
});
|
|
83
85
|
}
|
|
84
86
|
async listModels() {
|
|
85
|
-
|
|
87
|
+
const response = await this.request("/models");
|
|
88
|
+
return Array.isArray(response) ? response : response.data ?? [];
|
|
86
89
|
}
|
|
87
90
|
async getUsage(startDate, endDate, userId, groupBy) {
|
|
88
91
|
const params = new URLSearchParams({
|
|
@@ -186,15 +189,14 @@ var litellmPlugin = (0, import_backend_plugin_api.createBackendPlugin)({
|
|
|
186
189
|
register(reg) {
|
|
187
190
|
reg.registerInit({
|
|
188
191
|
deps: {
|
|
189
|
-
httpRouter:
|
|
190
|
-
config:
|
|
191
|
-
logger:
|
|
192
|
+
httpRouter: import_backend_plugin_api.coreServices.httpRouter,
|
|
193
|
+
config: import_backend_plugin_api.coreServices.rootConfig,
|
|
194
|
+
logger: import_backend_plugin_api.coreServices.logger,
|
|
195
|
+
auth: import_backend_plugin_api.coreServices.auth,
|
|
196
|
+
discovery: import_backend_plugin_api.coreServices.discovery
|
|
192
197
|
},
|
|
193
198
|
async init({ httpRouter, config, logger }) {
|
|
194
|
-
const router = await createRouter({
|
|
195
|
-
config,
|
|
196
|
-
logger
|
|
197
|
-
});
|
|
199
|
+
const router = await createRouter({ config, logger });
|
|
198
200
|
httpRouter.use(router);
|
|
199
201
|
}
|
|
200
202
|
});
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["export { litellmPlugin } from './plugin';\nexport { createRouter } from './router';\nexport * from './types';\nexport { LiteLLMClient } from './client';", "import { coreServices, createBackendPlugin } from '@backstage/backend-plugin-api';\nimport { createRouter } from './router';\n\nexport const litellmPlugin = createBackendPlugin({\n pluginId: 'litellm',\n register(reg) {\n reg.registerInit({\n deps: {\n httpRouter: coreServices.httpRouter,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n auth: coreServices.auth,\n discovery: coreServices.discovery,\n },\n async init({ httpRouter, config, logger }) {\n const router = await createRouter({ config, logger });\n httpRouter.use(router);\n },\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 const response = await this.request<{ info: VirtualKey[] } | VirtualKey[]>(`/key/info${query}`);\n return Array.isArray(response) ? response : (response.info ?? []);\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 const response = await this.request<{ data: ModelInfo[] } | ModelInfo[]>('/models');\n return Array.isArray(response) ? response : (response.data ?? []);\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}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.LiteLLMClient = exports.createRouter = exports.litellmPlugin = void 0;
|
|
18
|
+
var plugin_1 = require("./plugin");
|
|
19
|
+
Object.defineProperty(exports, "litellmPlugin", { enumerable: true, get: function () { return plugin_1.litellmPlugin; } });
|
|
20
|
+
var router_1 = require("./router");
|
|
21
|
+
Object.defineProperty(exports, "createRouter", { enumerable: true, get: function () { return router_1.createRouter; } });
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
23
|
+
var client_1 = require("./client");
|
|
24
|
+
Object.defineProperty(exports, "LiteLLMClient", { enumerable: true, get: function () { return client_1.LiteLLMClient; } });
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const litellmPlugin: import("@backstage/backend-plugin-api").BackendFeature;
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.litellmPlugin = void 0;
|
|
4
|
+
const backend_plugin_api_1 = require("@backstage/backend-plugin-api");
|
|
5
|
+
const router_1 = require("./router");
|
|
6
|
+
exports.litellmPlugin = (0, backend_plugin_api_1.createBackendPlugin)({
|
|
7
|
+
pluginId: 'litellm',
|
|
8
|
+
register(reg) {
|
|
9
|
+
reg.registerInit({
|
|
10
|
+
deps: {
|
|
11
|
+
httpRouter: backend_plugin_api_1.coreServices.httpRouter,
|
|
12
|
+
config: backend_plugin_api_1.coreServices.rootConfig,
|
|
13
|
+
logger: backend_plugin_api_1.coreServices.logger,
|
|
14
|
+
auth: backend_plugin_api_1.coreServices.auth,
|
|
15
|
+
discovery: backend_plugin_api_1.coreServices.discovery,
|
|
16
|
+
},
|
|
17
|
+
async init({ httpRouter, config, logger }) {
|
|
18
|
+
const router = await (0, router_1.createRouter)({ config, logger });
|
|
19
|
+
httpRouter.use(router);
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
});
|
package/dist/router.d.ts
ADDED
package/dist/router.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRouter = createRouter;
|
|
4
|
+
const express_1 = require("express");
|
|
5
|
+
const client_1 = require("./client");
|
|
6
|
+
async function createRouter(options) {
|
|
7
|
+
const { config, logger } = options;
|
|
8
|
+
const baseUrl = config.getString('litellm.baseUrl');
|
|
9
|
+
const masterKey = config.getString('litellm.masterKey');
|
|
10
|
+
const client = new client_1.LiteLLMClient({ baseUrl, masterKey });
|
|
11
|
+
const router = (0, express_1.Router)();
|
|
12
|
+
router.get('/health', (_req, res) => {
|
|
13
|
+
res.json({ status: 'ok' });
|
|
14
|
+
});
|
|
15
|
+
router.get('/user/info', async (req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const userId = req.query.user_id;
|
|
18
|
+
const userInfo = await client.getUserInfo(userId);
|
|
19
|
+
res.json(userInfo);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
logger.error('Failed to fetch user info', error);
|
|
23
|
+
res.status(500).json({ error: error.message });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
router.get('/keys', async (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const userId = req.query.user_id;
|
|
29
|
+
const keys = await client.listKeys(userId);
|
|
30
|
+
res.json(keys);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
logger.error('Failed to list keys', error);
|
|
34
|
+
res.status(500).json({ error: error.message });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
router.post('/keys/generate', async (req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
const request = req.body;
|
|
40
|
+
const result = await client.generateKey(request);
|
|
41
|
+
res.json(result);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
logger.error('Failed to generate key', error);
|
|
45
|
+
res.status(500).json({ error: error.message });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
router.delete('/keys/:keyId', async (req, res) => {
|
|
49
|
+
try {
|
|
50
|
+
const { keyId } = req.params;
|
|
51
|
+
if (!keyId) {
|
|
52
|
+
res.status(400).json({ error: 'keyId is required' });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
await client.deleteKeys({ keys: [keyId] });
|
|
56
|
+
res.json({ success: true });
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
logger.error('Failed to delete key', error);
|
|
60
|
+
res.status(500).json({ error: error.message });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
router.get('/models', async (_req, res) => {
|
|
64
|
+
try {
|
|
65
|
+
const models = await client.listModels();
|
|
66
|
+
res.json(models);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
logger.error('Failed to list models', error);
|
|
70
|
+
res.status(500).json({ error: error.message });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
router.get('/usage', async (req, res) => {
|
|
74
|
+
try {
|
|
75
|
+
const { start_date, end_date, user_id, group_by } = req.query;
|
|
76
|
+
if (!start_date || !end_date) {
|
|
77
|
+
res.status(400).json({ error: 'start_date and end_date are required' });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const usage = await client.getUsage(start_date, end_date, user_id, group_by);
|
|
81
|
+
res.json(usage);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
logger.error('Failed to fetch usage', error);
|
|
85
|
+
res.status(500).json({ error: error.message });
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return router;
|
|
89
|
+
}
|
package/dist/types.cjs.js
CHANGED
package/dist/types.cjs.js.map
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/types.ts"],
|
|
4
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": "
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/types.js
ADDED
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acarmisc/backstage-plugin-litellm-backend",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "The Backstage backend plugin for LiteLLM governance",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "backend-plugin",
|
|
7
7
|
"pluginId": "litellm",
|
|
8
8
|
"pluginPackages": [
|
|
9
|
-
"@
|
|
9
|
+
"@acarmisc/backstage-plugin-litellm-backend"
|
|
10
10
|
]
|
|
11
11
|
},
|
|
12
12
|
"publishConfig": {
|