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 configure it with daily or monthly limits for
90
- spending, tokens, or requests. Team and gateway limits apply to every app
91
- automatically, even when the app's chain doesn't include Budgets and Costs. See
92
- [Usage Limits](./usage-limits.mdx).
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
- - **Policies**—the team's [policy template](./policy-templates.mdx), the
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 & Thresholds"
2
+ title: "Usage Limits & Budget Rules"
3
3
  sidebar_label: "Usage Limits"
4
4
  description:
5
- Set hierarchical spending, token, and request limits at the gateway, team, and
6
- app levels, with warning thresholds and quota fallbacks.
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 level that blocked it:
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
- Each level supports the same three meters, each with independent daily and
34
- monthly settings:
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
- - **Budget** - spend in dollars
37
- - **Tokens** - input plus output tokens
38
- - **Requests** - request count
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
- For each meter and period you can set a **Limit** (usage at or above it gets
41
- blocked) and a **Warning** threshold (a percentage of the limit that triggers a
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
- Daily and monthly periods are anchored to your gateway, not the calendar: a
47
- daily period rolls over at the time of day (UTC) the gateway was created, and a
48
- monthly period runs anchor-day to anchor-day. Changing a limit mid-period
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. Set daily or monthly limits for budget, tokens, or requests, and optional
64
- warning thresholds.
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. The change applies within about a minute.
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
- :::note
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
- Each period in the Budgets and Costs policy's options carries an `enabled` flag
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
- If the app has a **quota fallback** model configured, an exceeded limit routes
82
- requests to that model instead of blocking with a `429`—see
83
- [Fallback Models](./fallback.mdx). The fallback's usage still counts toward the
84
- limits.
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
- :::note{title="App, team, and gateway limits are configured separately"}
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
- The Budgets and Costs policy configures and enforces an app's own limits. Team
89
- and gateway limits apply to every app automatically, whether or not the app's
90
- policy chain includes Budgets and Costs.
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 controls how an error
97
- while checking the app's own limits is handled. It defaults to `false`, so the
98
- request continues through the gateway. Set it to `true` to reject the request
99
- instead. Team and gateway limit checks run independently of this policy.
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",
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.3",
36
- "@zuplo/core": "7.6.3",
37
- "@zuplo/runtime": "7.6.3",
38
- "@zuplo/test": "7.6.3"
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
  }