@acarmisc/backstage-plugin-litellm-backend 0.6.0 → 0.6.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/bridge.d.ts +0 -10
- package/dist/bridge.js +0 -18
- package/dist/client.d.ts +0 -7
- package/dist/client.js +0 -9
- package/dist/index.cjs.js +1 -92
- package/dist/index.cjs.js.map +2 -2
- package/dist/openapi.js +0 -18
- package/dist/router.js +2 -37
- package/dist/types.cjs.js.map +1 -1
- package/dist/types.d.ts +0 -4
- package/package.json +1 -1
package/dist/openapi.js
CHANGED
|
@@ -105,8 +105,6 @@ exports.openApiSpec = {
|
|
|
105
105
|
max_budget: { type: 'number', nullable: true, description: 'Positive number caps spend; null = unlimited' },
|
|
106
106
|
tpm_limit: { type: 'number' },
|
|
107
107
|
rpm_limit: { type: 'number' },
|
|
108
|
-
auto_rotate: { type: 'boolean' },
|
|
109
|
-
rotation_interval_days: { type: 'number' },
|
|
110
108
|
} } } },
|
|
111
109
|
},
|
|
112
110
|
responses: {
|
|
@@ -123,14 +121,6 @@ exports.openApiSpec = {
|
|
|
123
121
|
responses: { '200': { description: 'Deleted' }, '403': { description: 'Not the key owner' } },
|
|
124
122
|
},
|
|
125
123
|
},
|
|
126
|
-
'/keys/{keyId}/regenerate': {
|
|
127
|
-
post: {
|
|
128
|
-
tags: ['Keys'],
|
|
129
|
-
summary: 'Rotate a key in place (caller must own it)',
|
|
130
|
-
parameters: [{ name: 'keyId', in: 'path', required: true, schema: { type: 'string' } }],
|
|
131
|
-
responses: { '200': { description: 'New secret' }, '403': { description: 'Not the key owner' } },
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
124
|
'/keys/{keyId}/update': {
|
|
135
125
|
post: {
|
|
136
126
|
tags: ['Keys'],
|
|
@@ -251,14 +241,6 @@ exports.openApiSpec = {
|
|
|
251
241
|
responses: { '200': { description: 'Generated key' }, '401': { description: 'Invalid token' } },
|
|
252
242
|
},
|
|
253
243
|
},
|
|
254
|
-
'/bridge/keys/regenerate': {
|
|
255
|
-
post: {
|
|
256
|
-
tags: ['Bridge'],
|
|
257
|
-
summary: 'Rotate the caller key by alias (Keycloak token auth)',
|
|
258
|
-
requestBody: { content: { 'application/json': { schema: { type: 'object', properties: { alias: { type: 'string' } } } } } },
|
|
259
|
-
responses: { '200': { description: 'New secret' }, '401': { description: 'Invalid token' }, '404': { description: 'No key with that alias' } },
|
|
260
|
-
},
|
|
261
|
-
},
|
|
262
244
|
'/bridge/models': {
|
|
263
245
|
get: {
|
|
264
246
|
tags: ['Bridge'],
|
package/dist/router.js
CHANGED
|
@@ -173,9 +173,8 @@ async function createRouter(options) {
|
|
|
173
173
|
// Every key-mutation route below runs under the LiteLLM master key, so
|
|
174
174
|
// without an explicit check any authenticated Backstage user could act on
|
|
175
175
|
// any key whose token they learn (audit logs expose truncated tokens, and
|
|
176
|
-
// anyone who has held the raw key knows it). We
|
|
177
|
-
//
|
|
178
|
-
// own key list and 403 if the target token isn't in it.
|
|
176
|
+
// anyone who has held the raw key knows it). We fetch the caller's own key
|
|
177
|
+
// list and 403 if the target token isn't in it.
|
|
179
178
|
//
|
|
180
179
|
// Returns the caller's LiteLLM user_id (when resolvable) so handlers can
|
|
181
180
|
// stamp it in logs without re-deriving it.
|
|
@@ -202,25 +201,6 @@ async function createRouter(options) {
|
|
|
202
201
|
}
|
|
203
202
|
return false;
|
|
204
203
|
}
|
|
205
|
-
router.post('/keys/:keyId/regenerate', async (req, res) => {
|
|
206
|
-
try {
|
|
207
|
-
const { keyId } = req.params;
|
|
208
|
-
if (!keyId) {
|
|
209
|
-
res.status(400).json({ error: 'keyId is required' });
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
const { tokenEntityRef } = await authorizeKeyAction(req, keyId);
|
|
213
|
-
const result = await client.regenerateKey(keyId);
|
|
214
|
-
logger.info({ action: 'key.rotate', userId: tokenEntityRef ?? 'unknown', keyId });
|
|
215
|
-
res.json(result);
|
|
216
|
-
}
|
|
217
|
-
catch (error) {
|
|
218
|
-
if (sendOwnershipError(error, res))
|
|
219
|
-
return;
|
|
220
|
-
logger.error('Failed to rotate key', error);
|
|
221
|
-
res.status(500).json({ error: error.message });
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
204
|
router.post('/keys/generate', async (req, res) => {
|
|
225
205
|
try {
|
|
226
206
|
// Only alias is hard-required. max_budget is optional: a positive
|
|
@@ -542,21 +522,6 @@ async function createRouter(options) {
|
|
|
542
522
|
handleBridgeError(error, res);
|
|
543
523
|
}
|
|
544
524
|
});
|
|
545
|
-
router.post('/bridge/keys/regenerate', async (req, res) => {
|
|
546
|
-
try {
|
|
547
|
-
const claims = await requireClaims(req);
|
|
548
|
-
const alias = (req.body ?? {}).alias?.trim();
|
|
549
|
-
if (!alias) {
|
|
550
|
-
res.status(400).json({ error: 'alias is required' });
|
|
551
|
-
return;
|
|
552
|
-
}
|
|
553
|
-
const result = await (0, bridge_1.bridgeRegenerateKey)(client, claims, provisioningEnabled, provisioningDefaults, logger, alias, userIdDomain);
|
|
554
|
-
res.json(result);
|
|
555
|
-
}
|
|
556
|
-
catch (error) {
|
|
557
|
-
handleBridgeError(error, res);
|
|
558
|
-
}
|
|
559
|
-
});
|
|
560
525
|
router.get('/bridge/models', async (req, res) => {
|
|
561
526
|
try {
|
|
562
527
|
await requireClaims(req); // authenticate only
|
package/dist/types.cjs.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/types.ts"],
|
|
4
|
-
"sourcesContent": ["export interface UserInfo {\n user_id: string;\n user_email?: string;\n email?: string;\n teams?: string[];\n models?: string[];\n max_budget?: number;\n spend?: number;\n current_spend?: number;\n soft_limit?: number;\n hard_limit?: number;\n /** Backstage-computed: true when the user is a member of litellm.audit.group */\n can_view_audit?: boolean;\n}\n\nexport interface TeamMember {\n user_id: string;\n role: 'admin' | 'user';\n}\n\nexport interface TeamInfo {\n team_id: string;\n team_alias?: string;\n max_budget?: number;\n spend: number;\n members_with_roles?: TeamMember[];\n models?: string[];\n tpm_limit?: number;\n rpm_limit?: number;\n}\n\nexport interface VirtualKey {\n key: string;\n token: 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 blocked?: boolean;\n}\n\n/**\n * Shape of a single entry inside LiteLLM's `/user/info` `keys` array.\n * Differs from VirtualKey: uses `expires` (not `expires_at`), exposes\n * both a hashed `token` and a masked `key_name`, and fields are nullable\n * rather than optional.\n */\nexport interface LiteLLMUserKey {\n token: string;\n key_name?: string;\n key_alias?: string | null;\n spend?: number;\n expires?: string | null;\n models?: string[];\n tpm_limit?: number | null;\n rpm_limit?: number | null;\n max_budget?: number | null;\n user_id?: string | null;\n team_id?: string | null;\n created_at: string;\n blocked?: boolean | null;\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 max_input_tokens?: number;\n max_output_tokens?: number;\n}\n\nexport interface UsageModelBreakdown {\n total_spend: number;\n total_tokens: number;\n prompt_tokens: number;\n completion_tokens: number;\n api_requests: number;\n successful_requests: number;\n failed_requests: number;\n}\n\nexport interface UsageKeyBreakdown {\n key_alias?: string;\n team_id?: string | null;\n models: string[];\n total_spend: number;\n total_tokens: number;\n prompt_tokens: number;\n completion_tokens: number;\n api_requests: number;\n successful_requests: number;\n failed_requests: number;\n}\n\nexport interface UsageDailyPoint {\n date: string;\n spend: number;\n total_tokens: number;\n prompt_tokens: number;\n completion_tokens: number;\n api_requests: number;\n successful_requests: number;\n failed_requests: number;\n}\n\nexport interface UsageDailyModelPoint {\n date: string;\n model: string;\n spend: number;\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n api_requests: number;\n successful_requests: number;\n failed_requests: number;\n}\n\nexport interface UsageMetrics {\n total_spend: number;\n total_tokens: number;\n prompt_tokens: number;\n completion_tokens: number;\n api_requests: number;\n successful_requests: number;\n failed_requests: number;\n usage_by_model: Record<string, UsageModelBreakdown>;\n usage_by_key: Record<string, UsageKeyBreakdown>;\n daily_usage: UsageDailyPoint[];\n daily_by_model: UsageDailyModelPoint[];\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 team_id?: string;\n key_type?: string;\n metadata?: Record<string, string>;\n
|
|
4
|
+
"sourcesContent": ["export interface UserInfo {\n user_id: string;\n user_email?: string;\n email?: string;\n teams?: string[];\n models?: string[];\n max_budget?: number;\n spend?: number;\n current_spend?: number;\n soft_limit?: number;\n hard_limit?: number;\n /** Backstage-computed: true when the user is a member of litellm.audit.group */\n can_view_audit?: boolean;\n}\n\nexport interface TeamMember {\n user_id: string;\n role: 'admin' | 'user';\n}\n\nexport interface TeamInfo {\n team_id: string;\n team_alias?: string;\n max_budget?: number;\n spend: number;\n members_with_roles?: TeamMember[];\n models?: string[];\n tpm_limit?: number;\n rpm_limit?: number;\n}\n\nexport interface VirtualKey {\n key: string;\n token: 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 blocked?: boolean;\n}\n\n/**\n * Shape of a single entry inside LiteLLM's `/user/info` `keys` array.\n * Differs from VirtualKey: uses `expires` (not `expires_at`), exposes\n * both a hashed `token` and a masked `key_name`, and fields are nullable\n * rather than optional.\n */\nexport interface LiteLLMUserKey {\n token: string;\n key_name?: string;\n key_alias?: string | null;\n spend?: number;\n expires?: string | null;\n models?: string[];\n tpm_limit?: number | null;\n rpm_limit?: number | null;\n max_budget?: number | null;\n user_id?: string | null;\n team_id?: string | null;\n created_at: string;\n blocked?: boolean | null;\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 max_input_tokens?: number;\n max_output_tokens?: number;\n}\n\nexport interface UsageModelBreakdown {\n total_spend: number;\n total_tokens: number;\n prompt_tokens: number;\n completion_tokens: number;\n api_requests: number;\n successful_requests: number;\n failed_requests: number;\n}\n\nexport interface UsageKeyBreakdown {\n key_alias?: string;\n team_id?: string | null;\n models: string[];\n total_spend: number;\n total_tokens: number;\n prompt_tokens: number;\n completion_tokens: number;\n api_requests: number;\n successful_requests: number;\n failed_requests: number;\n}\n\nexport interface UsageDailyPoint {\n date: string;\n spend: number;\n total_tokens: number;\n prompt_tokens: number;\n completion_tokens: number;\n api_requests: number;\n successful_requests: number;\n failed_requests: number;\n}\n\nexport interface UsageDailyModelPoint {\n date: string;\n model: string;\n spend: number;\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n api_requests: number;\n successful_requests: number;\n failed_requests: number;\n}\n\nexport interface UsageMetrics {\n total_spend: number;\n total_tokens: number;\n prompt_tokens: number;\n completion_tokens: number;\n api_requests: number;\n successful_requests: number;\n failed_requests: number;\n usage_by_model: Record<string, UsageModelBreakdown>;\n usage_by_key: Record<string, UsageKeyBreakdown>;\n daily_usage: UsageDailyPoint[];\n daily_by_model: UsageDailyModelPoint[];\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 team_id?: string;\n key_type?: string;\n metadata?: Record<string, string>;\n}\n\nexport interface UpdateKeyRequest {\n key: string;\n key_alias?: string;\n models?: string[];\n max_budget?: number;\n tpm_limit?: number;\n rpm_limit?: number;\n team_id?: string;\n duration?: 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}\n\nexport interface ProvisioningDefaults {\n maxBudget: number;\n budgetDuration: string;\n models: string[];\n teams: string[];\n tpmLimit?: number;\n rpmLimit?: number;\n /**\n * LiteLLM user role applied on /user/new. Defaults to \"internal_user\"\n * which grants self-service Create/Delete/View on the user's own keys.\n * Valid values: proxy_admin, proxy_admin_viewer, internal_user,\n * internal_user_viewer, team.\n */\n userRole?: string;\n metadata: Record<string, string>;\n}\n\nexport interface RoleConfig {\n group: string;\n maxBudget?: number;\n budgetDuration?: string;\n models?: string[];\n teams?: string[];\n tpmLimit?: number;\n rpmLimit?: number;\n userRole?: string;\n metadata?: Record<string, string>;\n}\n\nexport interface CreateUserRequest {\n user_id: string;\n user_email?: string;\n user_alias?: string;\n user_role?: string;\n max_budget?: number;\n budget_duration?: string;\n models?: string[];\n teams?: string[];\n tpm_limit?: number;\n rpm_limit?: number;\n metadata?: Record<string, string>;\n auto_create_key?: boolean;\n}\n\nexport interface CreateUserResponse {\n user_id: string;\n user_email?: string;\n max_budget?: number;\n models?: string[];\n teams?: string[];\n}\n\nexport interface AuditLogEntry {\n id: string;\n updated_at: string;\n changed_by?: string;\n changed_by_api_key?: string;\n action?: string;\n table_name?: string;\n object_id?: string;\n before_value?: Record<string, unknown> | null;\n updated_values?: Record<string, unknown> | null;\n}\n\nexport interface PaginatedAuditLogs {\n audit_logs: AuditLogEntry[];\n total: number;\n page: number;\n page_size: number;\n total_pages: number;\n}\n\nexport interface AuditLogsParams {\n page?: number;\n page_size?: number;\n start_date?: string;\n end_date?: string;\n action?: string;\n table_name?: string;\n changed_by?: string;\n sort_by?: string;\n sort_order?: 'asc' | 'desc';\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -137,10 +137,6 @@ export interface GenerateKeyRequest {
|
|
|
137
137
|
team_id?: string;
|
|
138
138
|
key_type?: string;
|
|
139
139
|
metadata?: Record<string, string>;
|
|
140
|
-
/** When true, LiteLLM rotates the key on a schedule. */
|
|
141
|
-
auto_rotate?: boolean;
|
|
142
|
-
/** Rotation interval in days (LiteLLM-enforced when auto_rotate is true). */
|
|
143
|
-
rotation_interval_days?: number;
|
|
144
140
|
}
|
|
145
141
|
export interface UpdateKeyRequest {
|
|
146
142
|
key: string;
|