zuplo 7.6.3 → 7.6.4
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.
|
@@ -86,10 +86,11 @@ over—see [Dynamic model routing](./cookbooks/dynamic-model-routing.mdx).
|
|
|
86
86
|
## Setting budgets
|
|
87
87
|
|
|
88
88
|
App budgets live on the **Budgets and Costs** policy in the app's chain. Open
|
|
89
|
-
the app's **Policies** tab and
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
the app's **Policies** tab and add budget rules capping spending, tokens, or
|
|
90
|
+
requests over an hourly, daily, weekly, or monthly period—either for the app as
|
|
91
|
+
a whole or per metadata value, so each caller gets its own budget. Team and
|
|
92
|
+
gateway limits apply to every app automatically, even when the app's chain
|
|
93
|
+
doesn't include Budgets and Costs. See [Usage Limits](./usage-limits.mdx).
|
|
93
94
|
|
|
94
95
|
## Editing an App
|
|
95
96
|
|
|
@@ -39,7 +39,7 @@ click **Create Sub-Team** in the team's header.
|
|
|
39
39
|
|
|
40
40
|
After creating a team, use its tabs to configure it:
|
|
41
41
|
|
|
42
|
-
- **
|
|
42
|
+
- **Policy Template**—the team's [policy template](./policy-templates.mdx), the
|
|
43
43
|
starting policy chain for apps created in the team.
|
|
44
44
|
- **Usage & Limits**—spending limits that apply to all of the team's apps
|
|
45
45
|
combined. See [Usage Limits](./usage-limits.mdx).
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "Usage Limits &
|
|
2
|
+
title: "Usage Limits & Budget Rules"
|
|
3
3
|
sidebar_label: "Usage Limits"
|
|
4
4
|
description:
|
|
5
|
-
Set
|
|
6
|
-
|
|
5
|
+
Set spending, token, and request budgets at the gateway, team, and app levels,
|
|
6
|
+
and give every metadata value its own separate budget using expression rules.
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
The Zuplo AI Gateway provides hierarchical usage limits and budget controls to
|
|
@@ -13,7 +13,8 @@ gateway, team, and app levels.
|
|
|
13
13
|
## Budget Hierarchy
|
|
14
14
|
|
|
15
15
|
Limits at every level apply together—a request is blocked when _any_ level's
|
|
16
|
-
limit is exceeded, and the error names the
|
|
16
|
+
limit is exceeded, and the error names whether the whole application or the
|
|
17
|
+
caller's own segment ran out:
|
|
17
18
|
|
|
18
19
|
- **Gateway** - Limits across the Zuplo project (for example, $1,000/day),
|
|
19
20
|
covering all teams and apps combined
|
|
@@ -24,28 +25,66 @@ limit is exceeded, and the error names the level that blocked it:
|
|
|
24
25
|
|
|
25
26
|
## Where limits are configured
|
|
26
27
|
|
|
27
|
-
| Level | Where |
|
|
28
|
-
| ------- | --------------------------------------------------------------------------------- |
|
|
29
|
-
| Gateway | **Settings → Usage Limits** |
|
|
30
|
-
| Team | The team's **Usage & Limits** tab |
|
|
31
|
-
| App | The **Budgets and Costs** policy in the app's [policy chain](./policy-chains.mdx) |
|
|
28
|
+
| Level | Where | Per-metadata budgets |
|
|
29
|
+
| ------- | --------------------------------------------------------------------------------- | -------------------- |
|
|
30
|
+
| Gateway | **Settings → Usage Limits** | No |
|
|
31
|
+
| Team | The team's **Usage & Limits** tab | No |
|
|
32
|
+
| App | The **Budgets and Costs** policy in the app's [policy chain](./policy-chains.mdx) | Yes |
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
Gateway and team limits are inherited: they apply to every app beneath them,
|
|
35
|
+
whether or not the app's chain includes Budgets and Costs. An app's own limits
|
|
36
|
+
live on that policy, which is also the only place you can budget per metadata
|
|
37
|
+
value.
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
:::note
|
|
40
|
+
|
|
41
|
+
A team's **Policy Template** tab also carries a **Budgets and Costs** policy.
|
|
42
|
+
That template seeds the policy chain of apps created in the team—it sets those
|
|
43
|
+
apps' limits, not the team's own. Use **Usage & Limits** to cap the team.
|
|
44
|
+
|
|
45
|
+
:::
|
|
46
|
+
|
|
47
|
+
## How a budget rule works
|
|
48
|
+
|
|
49
|
+
An app's budgets are a list of rules on the **Budgets and Costs** policy. Each
|
|
50
|
+
rule answers two questions—what it budgets, and what the caps are.
|
|
51
|
+
|
|
52
|
+
**Scope** decides who shares the budget:
|
|
53
|
+
|
|
54
|
+
| Scope | What it budgets |
|
|
55
|
+
| --------------- | ----------------------------------------------------------- |
|
|
56
|
+
| **This app** | One shared budget for all of the app's traffic |
|
|
57
|
+
| **By metadata** | A separate budget for every distinct value of an expression |
|
|
58
|
+
|
|
59
|
+
A **By metadata** rule gives each caller its own allowance. With the expression
|
|
60
|
+
`request.headers.get("x-user")` and a $5 daily cap, every distinct `x-user`
|
|
61
|
+
value gets its own $5 per day—one user exhausting their budget doesn't affect
|
|
62
|
+
anyone else. An app has a single shared budget, and can add several metadata
|
|
63
|
+
rules alongside it.
|
|
64
|
+
|
|
65
|
+
**Limit rows** set the caps. Each row is a meter, a period, an amount, and an
|
|
66
|
+
action:
|
|
67
|
+
|
|
68
|
+
| Field | Values |
|
|
69
|
+
| ---------- | ----------------------------------------------------------------- |
|
|
70
|
+
| **Meter** | Cost (dollars), Tokens (input plus output), Requests |
|
|
71
|
+
| **Period** | Hourly, Daily, Weekly, Monthly |
|
|
72
|
+
| **Limit** | An absolute amount in the meter's own unit |
|
|
73
|
+
| **Action** | **Block** rejects the request; **Warn** notifies without blocking |
|
|
74
|
+
|
|
75
|
+
Add as many rows as you need to cap several meters and periods at once. A Warn
|
|
76
|
+
row and a Block row on the same meter and period give you warn-then-block—warn
|
|
77
|
+
at $80, block at $100. The warn amount has to be the lower of the two.
|
|
39
78
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
warning notification without blocking). Leave a field empty to not enforce it.
|
|
79
|
+
The **Overview** tab charts daily and monthly usage only. Hourly and weekly rows
|
|
80
|
+
are enforced, but no meter on that tab tracks them.
|
|
43
81
|
|
|
44
82
|
:::note
|
|
45
83
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
84
|
+
Periods are anchored to your gateway, not the calendar. Every period rolls over
|
|
85
|
+
relative to the time (UTC) the gateway was created, so a daily period resets at
|
|
86
|
+
that hour rather than at midnight, and a monthly one on that day of the month.
|
|
87
|
+
Teams and apps created later use the same anchor. Changing a limit mid-period
|
|
49
88
|
doesn't reset the period's usage.
|
|
50
89
|
|
|
51
90
|
:::
|
|
@@ -60,43 +99,102 @@ doesn't reset the period's usage.
|
|
|
60
99
|
1. On the **Policies** tab, configure the **Budgets and Costs** policy (add it
|
|
61
100
|
from **Add Policy** if the chain doesn't have it).
|
|
62
101
|
|
|
63
|
-
1.
|
|
64
|
-
|
|
102
|
+
1. Select **Add rule**, choose the scope, and use **Add limit** to add rows for
|
|
103
|
+
the meters and periods you want to cap. For a **By metadata** rule, enter the
|
|
104
|
+
expression that identifies each caller.
|
|
65
105
|
|
|
66
|
-
1. Save
|
|
106
|
+
1. Select **Apply**, then **Save changes** on the policy chain. The change
|
|
107
|
+
applies within about a minute.
|
|
67
108
|
|
|
68
109
|
</Stepper>
|
|
69
110
|
|
|
70
|
-
|
|
111
|
+
Rules set on a parent team or on the gateway appear in the editor under
|
|
112
|
+
**Inherited**. Edit those where they were created.
|
|
71
113
|
|
|
72
|
-
|
|
73
|
-
next to its number, and so does each warning threshold. A period enforces
|
|
74
|
-
nothing until `enabled` is `true` _and_ `limit` is set—options that set only
|
|
75
|
-
`limit` are silently inert. Watch for this when editing the policy's options
|
|
76
|
-
directly, such as in `config/policies.json` or a
|
|
77
|
-
[policy template](./policy-templates.mdx).
|
|
114
|
+
### Budget expressions
|
|
78
115
|
|
|
79
|
-
|
|
116
|
+
A **By metadata** rule identifies each caller with an expression evaluated per
|
|
117
|
+
request. The editor validates it as you type. Available values include:
|
|
80
118
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
119
|
+
- `request.headers.get("x-user")` and other request headers
|
|
120
|
+
- `request.user.sub` and `request.user.data.<key>` from the authenticated API
|
|
121
|
+
key
|
|
122
|
+
- `request.url`, `request.method`, `request.query`, `request.searchParams`, and
|
|
123
|
+
`request.params`
|
|
124
|
+
- `context.contextId`, `context.requestId`, `context.route`, and
|
|
125
|
+
`context.custom`
|
|
85
126
|
|
|
86
|
-
|
|
127
|
+
The editor labels each expression by where its value comes from, because that
|
|
128
|
+
decides how much the budget can be trusted. A **client-supplied** value—a header
|
|
129
|
+
or query parameter—is attribution, not enforcement: a caller who changes it gets
|
|
130
|
+
a fresh budget. Budget on `request.user.*`, labeled **verified**, when the
|
|
131
|
+
segment itself has to hold, and keep a shared-budget rule alongside it as the
|
|
132
|
+
hard cap.
|
|
87
133
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
134
|
+
:::caution
|
|
135
|
+
|
|
136
|
+
Changing an expression starts a new budget. The old rule's usage stays with the
|
|
137
|
+
old expression, so every value's accounting restarts from zero. Expressions are
|
|
138
|
+
also case-sensitive: `get("X-User")` and `get("x-user")` read the same header
|
|
139
|
+
but budget separately.
|
|
91
140
|
|
|
92
141
|
:::
|
|
93
142
|
|
|
143
|
+
To write rules by hand in `config/policies.json` or a
|
|
144
|
+
[policy template](./policy-templates.mdx), see the
|
|
145
|
+
[Budgets and Costs policy reference](../policies/ai-gateway-metering-v2-inbound.mdx).
|
|
146
|
+
|
|
147
|
+
## Setting team and gateway limits
|
|
148
|
+
|
|
149
|
+
Team and gateway limits cover everything beneath them, and apply to every app
|
|
150
|
+
whether or not its chain includes Budgets and Costs.
|
|
151
|
+
|
|
152
|
+
Both use the same editor as an app, with the same limit rows—meter, period,
|
|
153
|
+
amount, and action. Open the team's **Usage & Limits** tab, or **Settings →
|
|
154
|
+
Usage Limits** for the gateway, then **Add rule** and **Add limit**.
|
|
155
|
+
|
|
156
|
+
Each level takes one shared budget covering everything beneath it: a team's caps
|
|
157
|
+
all of its apps combined, and the gateway's caps the whole project. Rules set
|
|
158
|
+
higher up appear under **Inherited**—edit those where they were created.
|
|
159
|
+
|
|
160
|
+
## When a limit is exceeded
|
|
161
|
+
|
|
162
|
+
If the app has a **quota fallback** model configured, an exceeded limit routes
|
|
163
|
+
requests to that model instead of blocking—see
|
|
164
|
+
[Fallback Models](./fallback.mdx), where the fallback model is selected. This
|
|
165
|
+
applies to gateway and team limits as well as the app's own. The fallback's
|
|
166
|
+
usage still counts toward the limits.
|
|
167
|
+
|
|
168
|
+
Without a fallback, the request is rejected with `429 Too Many Requests` and a
|
|
169
|
+
`budget` object naming what ran out:
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"type": "https://httpproblems.com/http-status/429",
|
|
174
|
+
"title": "Too Many Requests",
|
|
175
|
+
"detail": "Your daily cost budget has been exhausted (limit: 5).",
|
|
176
|
+
"budget": {
|
|
177
|
+
"scope": "dimension",
|
|
178
|
+
"dimensionedBy": "request.headers.get(\"x-user\")",
|
|
179
|
+
"ruleId": "3f2a9c1b",
|
|
180
|
+
"meter": "cost",
|
|
181
|
+
"window": "daily",
|
|
182
|
+
"limit": 5
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The `scope` tells the caller which budget they hit: `application` when a shared
|
|
188
|
+
budget ran out, or `dimension` when their own segment did—two situations with
|
|
189
|
+
different remedies. Quote the `ruleId` when raising a support request.
|
|
190
|
+
|
|
94
191
|
:::caution{title="Budgets fail open by default"}
|
|
95
192
|
|
|
96
|
-
The Budgets and Costs policy's `throwOnFailure` option
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
193
|
+
The Budgets and Costs policy's `throwOnFailure` option—**Fail closed when
|
|
194
|
+
metering is unavailable** in the editor—controls how an error while checking the
|
|
195
|
+
app's own limits is handled. It defaults to `false`, so the request continues
|
|
196
|
+
through the gateway. Set it to `true` to reject the request instead. Team and
|
|
197
|
+
gateway limit checks run independently of this policy.
|
|
100
198
|
|
|
101
199
|
:::
|
|
102
200
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zuplo",
|
|
3
|
-
"version": "7.6.
|
|
3
|
+
"version": "7.6.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The official Zuplo CLI for local development and platform management",
|
|
6
6
|
"homepage": "https://zuplo.com/docs/cli/overview",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"zuplo": "zuplo.js"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@zuplo/cli": "7.6.
|
|
36
|
-
"@zuplo/core": "7.6.
|
|
37
|
-
"@zuplo/runtime": "7.6.
|
|
38
|
-
"@zuplo/test": "7.6.
|
|
35
|
+
"@zuplo/cli": "7.6.4",
|
|
36
|
+
"@zuplo/core": "7.6.4",
|
|
37
|
+
"@zuplo/runtime": "7.6.4",
|
|
38
|
+
"@zuplo/test": "7.6.4"
|
|
39
39
|
}
|
|
40
40
|
}
|