@intutic/shared-types 2.3.0 → 2.4.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/dist/auth.d.ts +106 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +17 -1
- package/dist/auth.js.map +1 -1
- package/dist/cloudProbe.d.ts +54 -0
- package/dist/cloudProbe.d.ts.map +1 -0
- package/dist/cloudProbe.js +254 -0
- package/dist/cloudProbe.js.map +1 -0
- package/dist/enums.d.ts +48 -0
- package/dist/enums.d.ts.map +1 -1
- package/dist/enums.js +31 -3
- package/dist/enums.js.map +1 -1
- package/dist/finops.d.ts +38 -0
- package/dist/finops.d.ts.map +1 -1
- package/dist/guardrailIr.d.ts +6 -6
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -6
- package/dist/index.js.map +1 -1
- package/dist/mcpBudgets.d.ts +24 -24
- package/dist/notifications.d.ts +17 -2
- package/dist/notifications.d.ts.map +1 -1
- package/dist/notifications.js +26 -1
- package/dist/notifications.js.map +1 -1
- package/dist/providerVerification.d.ts +35 -9
- package/dist/providerVerification.d.ts.map +1 -1
- package/dist/providerVerification.js +79 -19
- package/dist/providerVerification.js.map +1 -1
- package/dist/providers.d.ts +50 -13
- package/dist/providers.d.ts.map +1 -1
- package/dist/providers.js +131 -27
- package/dist/providers.js.map +1 -1
- package/dist/spendBudgets.d.ts +122 -0
- package/dist/spendBudgets.d.ts.map +1 -0
- package/dist/spendBudgets.js +92 -0
- package/dist/spendBudgets.js.map +1 -0
- package/dist/upstreamRetry.d.ts +121 -0
- package/dist/upstreamRetry.d.ts.map +1 -0
- package/dist/upstreamRetry.js +90 -0
- package/dist/upstreamRetry.js.map +1 -0
- package/dist/wasmRuleDescriptor.d.ts +43 -0
- package/dist/wasmRuleDescriptor.d.ts.map +1 -0
- package/dist/wasmRuleDescriptor.js +65 -0
- package/dist/wasmRuleDescriptor.js.map +1 -0
- package/dist/workspaceSettings.d.ts +37 -0
- package/dist/workspaceSettings.d.ts.map +1 -1
- package/dist/workspaceSettings.js.map +1 -1
- package/package.json +2 -2
package/dist/providers.js
CHANGED
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
* new provider's *field layout* is a data change here, not new code in
|
|
13
13
|
* every place that touches credentials.
|
|
14
14
|
*
|
|
15
|
+
* AWS Bedrock, Google Vertex AI and Azure OpenAI are routed by the proxy's
|
|
16
|
+
* `src/cloud` module: requests name them as `bedrock/<model id>`,
|
|
17
|
+
* `vertex/<model>` and `azure/<deployment>`, and the stored blob is the
|
|
18
|
+
* region/endpoint plus credential that module signs or authenticates with.
|
|
19
|
+
*
|
|
15
20
|
* Deliberately NOT exhaustive — LiteLLM supports 100+ providers. This is a
|
|
16
21
|
* representative slice spanning the shapes that actually differ (single
|
|
17
22
|
* key, multi-field cloud credential, self-hosted endpoint, structured
|
|
@@ -20,21 +25,26 @@
|
|
|
20
25
|
* of these shapes is adding one entry, not new plumbing.
|
|
21
26
|
*
|
|
22
27
|
* `routingLive: true` means `packages/proxy` actually forwards requests to
|
|
23
|
-
* this provider today (`
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
28
|
+
* this provider today: it has an upstream for it (`provider_from_wire_id` in
|
|
29
|
+
* `proxy.rs`, which a proxy test holds this flag to). Gemini models are served
|
|
30
|
+
* on the Messages, Chat Completions and Responses routes, through the
|
|
31
|
+
* Gemini API translation; the proxy refuses the native `/v1beta/models/…`
|
|
32
|
+
* route, whose model is named in the URL. `routingLive: false` means the
|
|
33
|
+
* credential can be pre-provisioned here, but nothing in the proxy calls it
|
|
34
|
+
* yet — routing support is separate, real per-provider engineering (a new
|
|
35
|
+
* `Provider` enum variant, `get_model_provider`, pricing in `pricing.rs`),
|
|
36
|
+
* not a config change. The dashboard wizard must say so, not imply the key
|
|
37
|
+
* is live the moment it's saved.
|
|
30
38
|
*
|
|
31
39
|
* @module
|
|
32
40
|
*/
|
|
33
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
-
exports.DEEPSEEK_API_MODEL_IDS = exports.PROVIDER_REGISTRY = exports.LIVE_ROUTING_PROVIDER_IDS = void 0;
|
|
42
|
+
exports.AZURE_ENDPOINT_HOST_SUFFIXES = exports.DEEPSEEK_API_MODEL_IDS = exports.PROVIDER_REGISTRY = exports.LIVE_ROUTING_PROVIDER_IDS = void 0;
|
|
35
43
|
exports.getProviderDefinition = getProviderDefinition;
|
|
36
44
|
exports.isKnownProviderId = isKnownProviderId;
|
|
37
45
|
exports.inferProviderForModel = inferProviderForModel;
|
|
46
|
+
exports.cloudProviderForModel = cloudProviderForModel;
|
|
47
|
+
exports.checkCloudCredentialFields = checkCloudCredentialFields;
|
|
38
48
|
/**
|
|
39
49
|
* The 3 providers this system supported before this registry existed —
|
|
40
50
|
* their storage format (`{provider}_api_key`, a flat string field) is
|
|
@@ -75,34 +85,45 @@ exports.PROVIDER_REGISTRY = [
|
|
|
75
85
|
displayName: 'Google Gemini',
|
|
76
86
|
docsUrl: 'https://aistudio.google.com/app/apikey',
|
|
77
87
|
fields: [{ key: 'apiKey', label: 'API Key', type: 'password', required: true }],
|
|
78
|
-
|
|
79
|
-
// translating it to Gemini's generateContent format, so no request
|
|
80
|
-
// succeeds. The key keeps its flat-field storage
|
|
81
|
-
// (LIVE_ROUTING_PROVIDER_IDS), so a key saved now is the one the proxy
|
|
82
|
-
// reads once Gemini routing ships.
|
|
83
|
-
routingLive: false,
|
|
88
|
+
routingLive: true,
|
|
84
89
|
},
|
|
85
90
|
{
|
|
86
91
|
id: 'azure_openai',
|
|
87
92
|
displayName: 'Azure OpenAI',
|
|
88
|
-
docsUrl: 'https://learn.microsoft.com/azure/ai-
|
|
93
|
+
docsUrl: 'https://learn.microsoft.com/azure/ai-foundry/openai/how-to/create-resource',
|
|
89
94
|
fields: [
|
|
95
|
+
{
|
|
96
|
+
key: 'endpoint',
|
|
97
|
+
label: 'Resource Endpoint',
|
|
98
|
+
type: 'text',
|
|
99
|
+
required: true,
|
|
100
|
+
placeholder: 'https://your-resource.openai.azure.com',
|
|
101
|
+
helpText: 'An *.openai.azure.com, *.services.ai.azure.com or *.cognitiveservices.azure.com resource URL.',
|
|
102
|
+
},
|
|
90
103
|
{ key: 'apiKey', label: 'API Key', type: 'password', required: true },
|
|
91
|
-
{ key: 'endpoint', label: 'Resource Endpoint', type: 'text', required: true, placeholder: 'https://your-resource.openai.azure.com' },
|
|
92
|
-
{ key: 'deploymentName', label: 'Deployment Name', type: 'text', required: true },
|
|
93
104
|
],
|
|
94
|
-
|
|
105
|
+
usageHint: 'Name a deployment as azure/<deployment> in the model field.',
|
|
106
|
+
routingLive: true,
|
|
95
107
|
},
|
|
96
108
|
{
|
|
97
109
|
id: 'bedrock',
|
|
98
110
|
displayName: 'AWS Bedrock',
|
|
99
111
|
docsUrl: 'https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html',
|
|
100
112
|
fields: [
|
|
101
|
-
{ key: 'awsAccessKeyId', label: 'AWS Access Key ID', type: 'password', required: true },
|
|
102
|
-
{ key: 'awsSecretAccessKey', label: 'AWS Secret Access Key', type: 'password', required: true },
|
|
103
113
|
{ key: 'awsRegion', label: 'AWS Region', type: 'text', required: true, placeholder: 'us-east-1' },
|
|
114
|
+
{ key: 'awsAccessKeyId', label: 'AWS Access Key ID', type: 'password', required: false },
|
|
115
|
+
{ key: 'awsSecretAccessKey', label: 'AWS Secret Access Key', type: 'password', required: false },
|
|
116
|
+
{
|
|
117
|
+
key: 'apiKey',
|
|
118
|
+
label: 'Bedrock API Key',
|
|
119
|
+
type: 'password',
|
|
120
|
+
required: false,
|
|
121
|
+
helpText: 'Instead of an access key pair.',
|
|
122
|
+
},
|
|
104
123
|
],
|
|
105
|
-
|
|
124
|
+
requiresOneOf: [['awsAccessKeyId', 'awsSecretAccessKey'], ['apiKey']],
|
|
125
|
+
usageHint: 'Name a model as bedrock/<model id>, e.g. bedrock/anthropic.claude-opus-4-7.',
|
|
126
|
+
routingLive: true,
|
|
106
127
|
},
|
|
107
128
|
{
|
|
108
129
|
id: 'vertex_ai',
|
|
@@ -110,9 +131,11 @@ exports.PROVIDER_REGISTRY = [
|
|
|
110
131
|
docsUrl: 'https://cloud.google.com/vertex-ai/docs/authentication',
|
|
111
132
|
fields: [
|
|
112
133
|
{ key: 'projectId', label: 'GCP Project ID', type: 'text', required: true },
|
|
134
|
+
{ key: 'location', label: 'Location', type: 'text', required: false, placeholder: 'global' },
|
|
113
135
|
{ key: 'serviceAccountJson', label: 'Service Account JSON', type: 'textarea', required: true, helpText: 'The full JSON key file contents for a service account with Vertex AI access.' },
|
|
114
136
|
],
|
|
115
|
-
|
|
137
|
+
usageHint: 'Name a model as vertex/<model>, e.g. vertex/claude-sonnet-4-5@20250929 or vertex/gemini-2.5-pro.',
|
|
138
|
+
routingLive: true,
|
|
116
139
|
},
|
|
117
140
|
{
|
|
118
141
|
id: 'cohere',
|
|
@@ -178,13 +201,18 @@ exports.DEEPSEEK_API_MODEL_IDS = ['deepseek-chat', 'deepseek-reasoner', 'deepsee
|
|
|
178
201
|
* against the Rust source; if the heuristic changes there, change it here in
|
|
179
202
|
* the same commit.
|
|
180
203
|
*
|
|
181
|
-
* Order matters and matches the Rust exactly:
|
|
182
|
-
* (
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
204
|
+
* Order matters and matches the Rust exactly: a cloud prefix
|
|
205
|
+
* ({@link cloudProviderForModel}) → claude → gemini → '/' (OpenRouter's
|
|
206
|
+
* vendor/model namespacing — checked before the mistral prefixes so
|
|
207
|
+
* "mistralai/..." routes to OpenRouter) → an exact DeepSeek API id
|
|
208
|
+
* ({@link DEEPSEEK_API_MODEL_IDS}) → mistral prefixes → OpenAI as the
|
|
209
|
+
* default arm. A `config.yaml` model alias is not visible here: it lives in
|
|
210
|
+
* the proxy's own configuration.
|
|
186
211
|
*/
|
|
187
212
|
function inferProviderForModel(model) {
|
|
213
|
+
const cloud = cloudProviderForModel(model);
|
|
214
|
+
if (cloud)
|
|
215
|
+
return cloud;
|
|
188
216
|
const m = model.toLowerCase();
|
|
189
217
|
if (m.includes('claude'))
|
|
190
218
|
return 'anthropic';
|
|
@@ -199,4 +227,80 @@ function inferProviderForModel(model) {
|
|
|
199
227
|
}
|
|
200
228
|
return 'openai';
|
|
201
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* The cloud provider a `bedrock/`, `vertex/` (or `vertex_ai/`) or `azure/`
|
|
232
|
+
* model name routes to — `crate::cloud::parse_prefixed` in the proxy.
|
|
233
|
+
*/
|
|
234
|
+
function cloudProviderForModel(model) {
|
|
235
|
+
const slash = model.indexOf('/');
|
|
236
|
+
if (slash <= 0 || model.slice(slash + 1).trim() === '')
|
|
237
|
+
return undefined;
|
|
238
|
+
switch (model.slice(0, slash).toLowerCase()) {
|
|
239
|
+
case 'bedrock':
|
|
240
|
+
return 'bedrock';
|
|
241
|
+
case 'vertex':
|
|
242
|
+
case 'vertex_ai':
|
|
243
|
+
return 'vertex_ai';
|
|
244
|
+
case 'azure':
|
|
245
|
+
return 'azure_openai';
|
|
246
|
+
default:
|
|
247
|
+
return undefined;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/** Host suffixes a workspace's Azure endpoint may use — `AZURE_HOST_SUFFIXES` in the proxy. */
|
|
251
|
+
exports.AZURE_ENDPOINT_HOST_SUFFIXES = [
|
|
252
|
+
'.openai.azure.com',
|
|
253
|
+
'.services.ai.azure.com',
|
|
254
|
+
'.cognitiveservices.azure.com',
|
|
255
|
+
];
|
|
256
|
+
const CLOUD_NAME = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
257
|
+
/**
|
|
258
|
+
* The same checks the proxy applies when it reads a stored cloud credential
|
|
259
|
+
* (`cloud::config::from_workspace`), so a credential the proxy would refuse
|
|
260
|
+
* is refused when it is saved instead. Returns the reason, or `null`.
|
|
261
|
+
* Regions, locations and project ids become part of a hostname or URL path;
|
|
262
|
+
* the Azure endpoint is a host the gateway sends the key to, so it must be
|
|
263
|
+
* an Azure resource over TLS.
|
|
264
|
+
*/
|
|
265
|
+
function checkCloudCredentialFields(providerId, values) {
|
|
266
|
+
switch (providerId) {
|
|
267
|
+
case 'bedrock':
|
|
268
|
+
return CLOUD_NAME.test(values.awsRegion ?? '') ? null : 'AWS Region must be a region name such as us-east-1';
|
|
269
|
+
case 'vertex_ai': {
|
|
270
|
+
if (!/^[a-z0-9-]{1,64}$/.test(values.projectId ?? ''))
|
|
271
|
+
return 'GCP Project ID must be a project id or number';
|
|
272
|
+
if (values.location !== undefined && !CLOUD_NAME.test(values.location)) {
|
|
273
|
+
return 'Location must be a location name such as us-east5 or global';
|
|
274
|
+
}
|
|
275
|
+
let doc;
|
|
276
|
+
try {
|
|
277
|
+
doc = JSON.parse(values.serviceAccountJson ?? '');
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return 'Service Account JSON is not valid JSON';
|
|
281
|
+
}
|
|
282
|
+
const type = doc?.type;
|
|
283
|
+
return type === 'service_account' || type === 'authorized_user'
|
|
284
|
+
? null
|
|
285
|
+
: 'Service Account JSON must be a service_account (or authorized_user) credential file';
|
|
286
|
+
}
|
|
287
|
+
case 'azure_openai': {
|
|
288
|
+
let url;
|
|
289
|
+
try {
|
|
290
|
+
url = new URL(values.endpoint ?? '');
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return 'Resource Endpoint must be a URL';
|
|
294
|
+
}
|
|
295
|
+
const host = url.hostname.toLowerCase();
|
|
296
|
+
const label = exports.AZURE_ENDPOINT_HOST_SUFFIXES.map((sfx) => (host.endsWith(sfx) ? host.slice(0, -sfx.length) : null)).find((l) => l !== null);
|
|
297
|
+
const ok = url.protocol === 'https:' && url.port === '' && url.username === '' && !!label && /^[a-z0-9-]+$/.test(label);
|
|
298
|
+
return ok
|
|
299
|
+
? null
|
|
300
|
+
: 'Resource Endpoint must be https://<resource>.openai.azure.com, .services.ai.azure.com or .cognitiveservices.azure.com';
|
|
301
|
+
}
|
|
302
|
+
default:
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
202
306
|
//# sourceMappingURL=providers.js.map
|
package/dist/providers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spend budgets and key rate limits — how much LLM spend a workspace, one
|
|
3
|
+
* virtual key or one member may run up per day or per month, and how many
|
|
4
|
+
* requests and tokens a virtual key may send per minute.
|
|
5
|
+
*
|
|
6
|
+
* A budget is `hard` or `soft`. The LLM proxy refuses a request that a hard
|
|
7
|
+
* budget's remainder does not cover (`BUDGET_EXCEEDED`, 429, before the
|
|
8
|
+
* request leaves); a soft budget never refuses. Both raise the workspace's
|
|
9
|
+
* budget alerts — once at the alert threshold and once at the limit, per
|
|
10
|
+
* period — as `finops.budget.threshold` / `finops.budget.exceeded`.
|
|
11
|
+
*
|
|
12
|
+
* Periods are fixed UTC windows, like MCP call budgets (`mcpBudgets.ts`): a
|
|
13
|
+
* day starts at 00:00 UTC, a month on the 1st at 00:00 UTC. The window id is
|
|
14
|
+
* part of every per-key and per-member spend counter's Valkey key, so a new
|
|
15
|
+
* day or month starts from zero and an old counter expires on its own.
|
|
16
|
+
*
|
|
17
|
+
* Spend is what the control plane records per completed call
|
|
18
|
+
* (`finopsService.incrementSpend`), so a request's own cost lands after it
|
|
19
|
+
* finishes: the proxy checks a hard budget against the spend recorded so far
|
|
20
|
+
* plus the request's estimated cost, with the same 20% margin the workspace
|
|
21
|
+
* cap has always used.
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
import { z } from 'zod';
|
|
26
|
+
/** A budget's period: a UTC calendar day or a UTC calendar month. */
|
|
27
|
+
export type SpendBudgetPeriod = 'day' | 'month';
|
|
28
|
+
/** `hard` refuses requests the remainder does not cover; `soft` only alerts. */
|
|
29
|
+
export type SpendBudgetEnforcement = 'hard' | 'soft';
|
|
30
|
+
/**
|
|
31
|
+
* Whose spend a budget limits:
|
|
32
|
+
* - `workspace` every call in the workspace (Settings › Billing › Budget Limits);
|
|
33
|
+
* - `key` the calls one virtual key makes;
|
|
34
|
+
* - `member` the calls made with the virtual keys one member owns.
|
|
35
|
+
*/
|
|
36
|
+
export type SpendBudgetScope = 'workspace' | 'key' | 'member';
|
|
37
|
+
export interface SpendBudget {
|
|
38
|
+
period: SpendBudgetPeriod;
|
|
39
|
+
limitUsd: number;
|
|
40
|
+
enforcement: SpendBudgetEnforcement;
|
|
41
|
+
}
|
|
42
|
+
export declare const SPEND_BUDGET_PERIODS: readonly SpendBudgetPeriod[];
|
|
43
|
+
export declare const SPEND_BUDGET_SCOPES: readonly SpendBudgetScope[];
|
|
44
|
+
export declare const MAX_SPEND_BUDGET_USD = 1000000;
|
|
45
|
+
/**
|
|
46
|
+
* The member budget that applies to every member without one of their own,
|
|
47
|
+
* period by period. A member's own day budget replaces the default day
|
|
48
|
+
* budget; the default month budget still applies to them unless they have a
|
|
49
|
+
* month budget too.
|
|
50
|
+
*/
|
|
51
|
+
export declare const MEMBER_BUDGET_DEFAULT = "default";
|
|
52
|
+
export declare const SpendBudgetSchema: z.ZodObject<{
|
|
53
|
+
period: z.ZodEnum<["day", "month"]>;
|
|
54
|
+
limitUsd: z.ZodNumber;
|
|
55
|
+
enforcement: z.ZodDefault<z.ZodEnum<["hard", "soft"]>>;
|
|
56
|
+
}, "strict", z.ZodTypeAny, {
|
|
57
|
+
period: "day" | "month";
|
|
58
|
+
limitUsd: number;
|
|
59
|
+
enforcement: "hard" | "soft";
|
|
60
|
+
}, {
|
|
61
|
+
period: "day" | "month";
|
|
62
|
+
limitUsd: number;
|
|
63
|
+
enforcement?: "hard" | "soft" | undefined;
|
|
64
|
+
}>;
|
|
65
|
+
/** One budget per period at most: a key or member has a day budget, a month budget, both or neither. */
|
|
66
|
+
export declare const SpendBudgetListSchema: z.ZodEffects<z.ZodArray<z.ZodObject<{
|
|
67
|
+
period: z.ZodEnum<["day", "month"]>;
|
|
68
|
+
limitUsd: z.ZodNumber;
|
|
69
|
+
enforcement: z.ZodDefault<z.ZodEnum<["hard", "soft"]>>;
|
|
70
|
+
}, "strict", z.ZodTypeAny, {
|
|
71
|
+
period: "day" | "month";
|
|
72
|
+
limitUsd: number;
|
|
73
|
+
enforcement: "hard" | "soft";
|
|
74
|
+
}, {
|
|
75
|
+
period: "day" | "month";
|
|
76
|
+
limitUsd: number;
|
|
77
|
+
enforcement?: "hard" | "soft" | undefined;
|
|
78
|
+
}>, "many">, {
|
|
79
|
+
period: "day" | "month";
|
|
80
|
+
limitUsd: number;
|
|
81
|
+
enforcement: "hard" | "soft";
|
|
82
|
+
}[], {
|
|
83
|
+
period: "day" | "month";
|
|
84
|
+
limitUsd: number;
|
|
85
|
+
enforcement?: "hard" | "soft" | undefined;
|
|
86
|
+
}[]>;
|
|
87
|
+
/** Requests and tokens one virtual key may send per minute; `null` (or absent) means no limit. */
|
|
88
|
+
export interface KeyRateLimit {
|
|
89
|
+
rpm: number | null;
|
|
90
|
+
tpm: number | null;
|
|
91
|
+
}
|
|
92
|
+
export declare const MAX_KEY_RPM = 100000;
|
|
93
|
+
export declare const MAX_KEY_TPM = 100000000;
|
|
94
|
+
export declare const KeyRateLimitSchema: z.ZodObject<{
|
|
95
|
+
rpm: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
96
|
+
tpm: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
97
|
+
}, "strict", z.ZodTypeAny, {
|
|
98
|
+
rpm?: number | null | undefined;
|
|
99
|
+
tpm?: number | null | undefined;
|
|
100
|
+
}, {
|
|
101
|
+
rpm?: number | null | undefined;
|
|
102
|
+
tpm?: number | null | undefined;
|
|
103
|
+
}>;
|
|
104
|
+
/**
|
|
105
|
+
* One hard budget as the proxy reads it from a virtual key's cached auth
|
|
106
|
+
* entry (`hardBudgets`) and from `GET /api/v1/auth/key-context`. Soft budgets
|
|
107
|
+
* are not sent: the proxy never refuses on one.
|
|
108
|
+
*/
|
|
109
|
+
export interface HardBudgetWire {
|
|
110
|
+
scope: SpendBudgetScope;
|
|
111
|
+
period: SpendBudgetPeriod;
|
|
112
|
+
limitUsd: number;
|
|
113
|
+
}
|
|
114
|
+
/** The UTC window a moment falls in: its id (part of the counter key), when it began and when it resets. */
|
|
115
|
+
export declare function spendBudgetWindow(period: SpendBudgetPeriod, nowMs: number): {
|
|
116
|
+
id: string;
|
|
117
|
+
startMs: number;
|
|
118
|
+
resetAtMs: number;
|
|
119
|
+
};
|
|
120
|
+
/** A budget in words, for alerts, refusals and the dashboard. */
|
|
121
|
+
export declare function describeSpendBudget(scope: SpendBudgetScope, budget: SpendBudget, subjectLabel?: string): string;
|
|
122
|
+
//# sourceMappingURL=spendBudgets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spendBudgets.d.ts","sourceRoot":"","sources":["../src/spendBudgets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,OAAO,CAAA;AAE/C,gFAAgF;AAChF,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,MAAM,CAAA;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,KAAK,GAAG,QAAQ,CAAA;AAE7D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,iBAAiB,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,sBAAsB,CAAA;CACpC;AAED,eAAO,MAAM,oBAAoB,EAAE,SAAS,iBAAiB,EAAqB,CAAA;AAClF,eAAO,MAAM,mBAAmB,EAAE,SAAS,gBAAgB,EAAmC,CAAA;AAC9F,eAAO,MAAM,oBAAoB,UAAY,CAAA;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,YAAY,CAAA;AAE9C,eAAO,MAAM,iBAAiB;;;;;;;;;;;;EAMnB,CAAA;AAEX,wGAAwG;AACxG,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;IAS9B,CAAA;AAEJ,kGAAkG;AAClG,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CACnB;AAED,eAAO,MAAM,WAAW,SAAU,CAAA;AAClC,eAAO,MAAM,WAAW,YAAc,CAAA;AAEtC,eAAO,MAAM,kBAAkB;;;;;;;;;EAKpB,CAAA;AAEX;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,gBAAgB,CAAA;IACvB,MAAM,EAAE,iBAAiB,CAAA;IACzB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,4GAA4G;AAC5G,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,GACZ;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAUpD;AAED,iEAAiE;AACjE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAW/G"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Spend budgets and key rate limits — how much LLM spend a workspace, one
|
|
4
|
+
* virtual key or one member may run up per day or per month, and how many
|
|
5
|
+
* requests and tokens a virtual key may send per minute.
|
|
6
|
+
*
|
|
7
|
+
* A budget is `hard` or `soft`. The LLM proxy refuses a request that a hard
|
|
8
|
+
* budget's remainder does not cover (`BUDGET_EXCEEDED`, 429, before the
|
|
9
|
+
* request leaves); a soft budget never refuses. Both raise the workspace's
|
|
10
|
+
* budget alerts — once at the alert threshold and once at the limit, per
|
|
11
|
+
* period — as `finops.budget.threshold` / `finops.budget.exceeded`.
|
|
12
|
+
*
|
|
13
|
+
* Periods are fixed UTC windows, like MCP call budgets (`mcpBudgets.ts`): a
|
|
14
|
+
* day starts at 00:00 UTC, a month on the 1st at 00:00 UTC. The window id is
|
|
15
|
+
* part of every per-key and per-member spend counter's Valkey key, so a new
|
|
16
|
+
* day or month starts from zero and an old counter expires on its own.
|
|
17
|
+
*
|
|
18
|
+
* Spend is what the control plane records per completed call
|
|
19
|
+
* (`finopsService.incrementSpend`), so a request's own cost lands after it
|
|
20
|
+
* finishes: the proxy checks a hard budget against the spend recorded so far
|
|
21
|
+
* plus the request's estimated cost, with the same 20% margin the workspace
|
|
22
|
+
* cap has always used.
|
|
23
|
+
*
|
|
24
|
+
* @module
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.KeyRateLimitSchema = exports.MAX_KEY_TPM = exports.MAX_KEY_RPM = exports.SpendBudgetListSchema = exports.SpendBudgetSchema = exports.MEMBER_BUDGET_DEFAULT = exports.MAX_SPEND_BUDGET_USD = exports.SPEND_BUDGET_SCOPES = exports.SPEND_BUDGET_PERIODS = void 0;
|
|
28
|
+
exports.spendBudgetWindow = spendBudgetWindow;
|
|
29
|
+
exports.describeSpendBudget = describeSpendBudget;
|
|
30
|
+
const zod_1 = require("zod");
|
|
31
|
+
exports.SPEND_BUDGET_PERIODS = ['day', 'month'];
|
|
32
|
+
exports.SPEND_BUDGET_SCOPES = ['workspace', 'key', 'member'];
|
|
33
|
+
exports.MAX_SPEND_BUDGET_USD = 1_000_000;
|
|
34
|
+
/**
|
|
35
|
+
* The member budget that applies to every member without one of their own,
|
|
36
|
+
* period by period. A member's own day budget replaces the default day
|
|
37
|
+
* budget; the default month budget still applies to them unless they have a
|
|
38
|
+
* month budget too.
|
|
39
|
+
*/
|
|
40
|
+
exports.MEMBER_BUDGET_DEFAULT = 'default';
|
|
41
|
+
exports.SpendBudgetSchema = zod_1.z
|
|
42
|
+
.object({
|
|
43
|
+
period: zod_1.z.enum(['day', 'month']),
|
|
44
|
+
limitUsd: zod_1.z.number().positive().max(exports.MAX_SPEND_BUDGET_USD),
|
|
45
|
+
enforcement: zod_1.z.enum(['hard', 'soft']).default('hard'),
|
|
46
|
+
})
|
|
47
|
+
.strict();
|
|
48
|
+
/** One budget per period at most: a key or member has a day budget, a month budget, both or neither. */
|
|
49
|
+
exports.SpendBudgetListSchema = zod_1.z
|
|
50
|
+
.array(exports.SpendBudgetSchema)
|
|
51
|
+
.max(2)
|
|
52
|
+
.superRefine((list, ctx) => {
|
|
53
|
+
const seen = new Set();
|
|
54
|
+
list.forEach((b, i) => {
|
|
55
|
+
if (seen.has(b.period))
|
|
56
|
+
ctx.addIssue({ code: 'custom', path: [i, 'period'], message: `two ${b.period} budgets` });
|
|
57
|
+
seen.add(b.period);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
exports.MAX_KEY_RPM = 100_000;
|
|
61
|
+
exports.MAX_KEY_TPM = 100_000_000;
|
|
62
|
+
exports.KeyRateLimitSchema = zod_1.z
|
|
63
|
+
.object({
|
|
64
|
+
rpm: zod_1.z.number().int().min(1).max(exports.MAX_KEY_RPM).nullable().optional(),
|
|
65
|
+
tpm: zod_1.z.number().int().min(1).max(exports.MAX_KEY_TPM).nullable().optional(),
|
|
66
|
+
})
|
|
67
|
+
.strict();
|
|
68
|
+
/** The UTC window a moment falls in: its id (part of the counter key), when it began and when it resets. */
|
|
69
|
+
function spendBudgetWindow(period, nowMs) {
|
|
70
|
+
const d = new Date(nowMs);
|
|
71
|
+
const y = d.getUTCFullYear();
|
|
72
|
+
const m = d.getUTCMonth();
|
|
73
|
+
if (period === 'day') {
|
|
74
|
+
const startMs = Date.UTC(y, m, d.getUTCDate());
|
|
75
|
+
return { id: new Date(startMs).toISOString().slice(0, 10), startMs, resetAtMs: startMs + 86_400_000 };
|
|
76
|
+
}
|
|
77
|
+
const startMs = Date.UTC(y, m, 1);
|
|
78
|
+
return { id: new Date(startMs).toISOString().slice(0, 7), startMs, resetAtMs: Date.UTC(y, m + 1, 1) };
|
|
79
|
+
}
|
|
80
|
+
/** A budget in words, for alerts, refusals and the dashboard. */
|
|
81
|
+
function describeSpendBudget(scope, budget, subjectLabel) {
|
|
82
|
+
const whose = scope === 'workspace'
|
|
83
|
+
? 'Workspace'
|
|
84
|
+
: scope === 'key'
|
|
85
|
+
? `Key ${subjectLabel ?? ''}`.trim()
|
|
86
|
+
: subjectLabel === exports.MEMBER_BUDGET_DEFAULT || subjectLabel === undefined
|
|
87
|
+
? 'Member'
|
|
88
|
+
: `Member ${subjectLabel}`;
|
|
89
|
+
const per = budget.period === 'day' ? 'day' : 'month';
|
|
90
|
+
return `${whose} ${budget.enforcement} budget: $${budget.limitUsd.toFixed(2)} per ${per}`;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=spendBudgets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spendBudgets.js","sourceRoot":"","sources":["../src/spendBudgets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The workspace setting `upstreamRetry`: how the LLM proxy retries a provider
|
|
3
|
+
* call that failed before any response reached the client, and where it falls
|
|
4
|
+
* back when those retries run out.
|
|
5
|
+
*
|
|
6
|
+
* Every field is optional and overrides the proxy's own `config.yaml` value
|
|
7
|
+
* (`intutic_settings.routing.retry` / `.fallbacks`) field by field, so a
|
|
8
|
+
* workspace can change one knob without restating the rest. The proxy reads
|
|
9
|
+
* it from `/api/v1/auth/key-context` (`packages/proxy/src/routing/retry.rs`,
|
|
10
|
+
* `WorkspaceRetry`); `fixtures/upstream-retry-vectors.json` holds the two
|
|
11
|
+
* readings together.
|
|
12
|
+
*/
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
/**
|
|
15
|
+
* The ceilings both the schema and the proxy enforce, so no setting can
|
|
16
|
+
* configure a retry storm. The proxy clamps `config.yaml` to the same values
|
|
17
|
+
* (`RetryConfig::bounded`); a test holds the two equal.
|
|
18
|
+
*/
|
|
19
|
+
export declare const UPSTREAM_RETRY_LIMITS: {
|
|
20
|
+
readonly maxAttempts: 5;
|
|
21
|
+
readonly maxBackoffMs: 60000;
|
|
22
|
+
readonly budgetMs: 120000;
|
|
23
|
+
readonly fallbackTargets: 5;
|
|
24
|
+
readonly fallbackModels: 50;
|
|
25
|
+
};
|
|
26
|
+
/** Retried by default: 429, 500, 502, 503, 504 and Anthropic's 529 overloaded. */
|
|
27
|
+
export declare const DEFAULT_UPSTREAM_RETRY_STATUSES: readonly [429, 500, 502, 503, 504, 529];
|
|
28
|
+
/** The defaults the proxy applies when neither config.yaml nor the workspace sets a field. */
|
|
29
|
+
export declare const DEFAULT_UPSTREAM_RETRY: {
|
|
30
|
+
readonly enabled: true;
|
|
31
|
+
readonly maxAttempts: 3;
|
|
32
|
+
readonly initialBackoffMs: 500;
|
|
33
|
+
readonly maxBackoffMs: 8000;
|
|
34
|
+
readonly budgetMs: 30000;
|
|
35
|
+
readonly onStatus: readonly [429, 500, 502, 503, 504, 529];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Providers a fallback target may name: every id the proxy resolves
|
|
39
|
+
* (`provider_from_wire_id` in packages/proxy/src/proxy.rs), the cloud
|
|
40
|
+
* providers' aliases included, so a target copied from config.yaml is valid
|
|
41
|
+
* here too. A test holds the two lists equal.
|
|
42
|
+
*/
|
|
43
|
+
export declare const UPSTREAM_PROVIDERS: readonly ["anthropic", "openai", "gemini", "mistral", "openrouter", "deepseek", "bedrock", "vertex_ai", "vertex", "azure_openai", "azure"];
|
|
44
|
+
export type UpstreamProvider = (typeof UPSTREAM_PROVIDERS)[number];
|
|
45
|
+
/**
|
|
46
|
+
* One fallback target. `model` alone switches model on the provider that
|
|
47
|
+
* model resolves to; `provider` alone sends the same model to another
|
|
48
|
+
* provider; both pin the pair.
|
|
49
|
+
*/
|
|
50
|
+
export declare const UpstreamFallbackTargetSchema: z.ZodEffects<z.ZodObject<{
|
|
51
|
+
model: z.ZodOptional<z.ZodString>;
|
|
52
|
+
provider: z.ZodOptional<z.ZodEnum<["anthropic", "openai", "gemini", "mistral", "openrouter", "deepseek", "bedrock", "vertex_ai", "vertex", "azure_openai", "azure"]>>;
|
|
53
|
+
}, "strict", z.ZodTypeAny, {
|
|
54
|
+
model?: string | undefined;
|
|
55
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
56
|
+
}, {
|
|
57
|
+
model?: string | undefined;
|
|
58
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
59
|
+
}>, {
|
|
60
|
+
model?: string | undefined;
|
|
61
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
62
|
+
}, {
|
|
63
|
+
model?: string | undefined;
|
|
64
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
65
|
+
}>;
|
|
66
|
+
export type UpstreamFallbackTarget = z.infer<typeof UpstreamFallbackTargetSchema>;
|
|
67
|
+
export declare const UpstreamRetrySettingsSchema: z.ZodObject<{
|
|
68
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
69
|
+
maxAttempts: z.ZodOptional<z.ZodNumber>;
|
|
70
|
+
initialBackoffMs: z.ZodOptional<z.ZodNumber>;
|
|
71
|
+
maxBackoffMs: z.ZodOptional<z.ZodNumber>;
|
|
72
|
+
budgetMs: z.ZodOptional<z.ZodNumber>;
|
|
73
|
+
onStatus: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
|
|
74
|
+
/** Ordered targets, keyed by the model whose retries ran out. */
|
|
75
|
+
fallbacks: z.ZodOptional<z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodEffects<z.ZodObject<{
|
|
76
|
+
model: z.ZodOptional<z.ZodString>;
|
|
77
|
+
provider: z.ZodOptional<z.ZodEnum<["anthropic", "openai", "gemini", "mistral", "openrouter", "deepseek", "bedrock", "vertex_ai", "vertex", "azure_openai", "azure"]>>;
|
|
78
|
+
}, "strict", z.ZodTypeAny, {
|
|
79
|
+
model?: string | undefined;
|
|
80
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
81
|
+
}, {
|
|
82
|
+
model?: string | undefined;
|
|
83
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
84
|
+
}>, {
|
|
85
|
+
model?: string | undefined;
|
|
86
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
87
|
+
}, {
|
|
88
|
+
model?: string | undefined;
|
|
89
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
90
|
+
}>, "many">>, Record<string, {
|
|
91
|
+
model?: string | undefined;
|
|
92
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
93
|
+
}[]>, Record<string, {
|
|
94
|
+
model?: string | undefined;
|
|
95
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
96
|
+
}[]>>>;
|
|
97
|
+
}, "strict", z.ZodTypeAny, {
|
|
98
|
+
enabled?: boolean | undefined;
|
|
99
|
+
maxAttempts?: number | undefined;
|
|
100
|
+
initialBackoffMs?: number | undefined;
|
|
101
|
+
maxBackoffMs?: number | undefined;
|
|
102
|
+
budgetMs?: number | undefined;
|
|
103
|
+
onStatus?: number[] | undefined;
|
|
104
|
+
fallbacks?: Record<string, {
|
|
105
|
+
model?: string | undefined;
|
|
106
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
107
|
+
}[]> | undefined;
|
|
108
|
+
}, {
|
|
109
|
+
enabled?: boolean | undefined;
|
|
110
|
+
maxAttempts?: number | undefined;
|
|
111
|
+
initialBackoffMs?: number | undefined;
|
|
112
|
+
maxBackoffMs?: number | undefined;
|
|
113
|
+
budgetMs?: number | undefined;
|
|
114
|
+
onStatus?: number[] | undefined;
|
|
115
|
+
fallbacks?: Record<string, {
|
|
116
|
+
model?: string | undefined;
|
|
117
|
+
provider?: "anthropic" | "openai" | "mistral" | "openrouter" | "deepseek" | "gemini" | "azure_openai" | "bedrock" | "vertex_ai" | "vertex" | "azure" | undefined;
|
|
118
|
+
}[]> | undefined;
|
|
119
|
+
}>;
|
|
120
|
+
export type UpstreamRetrySettings = z.infer<typeof UpstreamRetrySettingsSchema>;
|
|
121
|
+
//# sourceMappingURL=upstreamRetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upstreamRetry.d.ts","sourceRoot":"","sources":["../src/upstreamRetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;;;;;CAMxB,CAAA;AAEV,kFAAkF;AAClF,eAAO,MAAM,+BAA+B,yCAA0C,CAAA;AAEtF,8FAA8F;AAC9F,eAAO,MAAM,sBAAsB;;;;;;;CAOzB,CAAA;AAEV;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,4IAYrB,CAAA;AACV,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAA;AAElE;;;;GAIG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;EAQrC,CAAA;AACJ,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAA;AAEjF,eAAO,MAAM,2BAA2B;;;;;;;IAQpC,iEAAiE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW1D,CAAA;AACX,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAA"}
|