zuplo 7.6.2 → 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.
- package/docs/ai-gateway/managing-apps.mdx +5 -4
- package/docs/ai-gateway/managing-providers.mdx +10 -6
- package/docs/ai-gateway/managing-teams.mdx +1 -1
- package/docs/ai-gateway/overview.mdx +5 -5
- package/docs/ai-gateway/providers.mdx +31 -2
- package/docs/ai-gateway/usage-limits.mdx +141 -43
- package/docs/ai-gateway/vertex-ai.mdx +389 -0
- package/package.json +5 -5
|
@@ -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
|
|
|
@@ -51,9 +51,11 @@ To add a new AI provider to your Zuplo AI Gateway, follow these steps:
|
|
|
51
51
|
create an API key for each provider see the
|
|
52
52
|
[provider documentation](./providers.mdx). The
|
|
53
53
|
[Zuplo Demo provider](./providers.mdx#zuplo-demo) asks for no API key—your
|
|
54
|
-
gateway authenticates to the demo service itself,
|
|
54
|
+
gateway authenticates to the demo service itself,
|
|
55
55
|
[Bedrock Mantle](./bedrock-mantle.mdx) additionally asks for an **AWS
|
|
56
|
-
Region** and accepts only long-term Bedrock API keys
|
|
56
|
+
Region** and accepts only long-term Bedrock API keys, and
|
|
57
|
+
[Vertex AI](./vertex-ai.mdx) asks for a **Location** and a **Google Cloud
|
|
58
|
+
Project ID** and takes a service account JSON key file instead of an API key.
|
|
57
59
|
|
|
58
60
|
1. Select the model or models you want to use with this provider. The available
|
|
59
61
|
models will depend on the selected provider. This can be changed later.
|
|
@@ -83,10 +85,12 @@ To modify an existing provider, open
|
|
|
83
85
|
and click the **Edit** button next to the provider you want to modify.
|
|
84
86
|
|
|
85
87
|
You can modify the API key and selected models for the provider—and, for
|
|
86
|
-
[Bedrock Mantle](./bedrock-mantle.mdx), the AWS Region
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
[Bedrock Mantle](./bedrock-mantle.mdx), the AWS Region, or for
|
|
89
|
+
[Vertex AI](./vertex-ai.mdx), the Location and Google Cloud Project ID. The
|
|
90
|
+
**Provider Name** isn't editable: it's the routing address in every
|
|
91
|
+
`providerName/model` reference, so renaming it would orphan each stored
|
|
92
|
+
reference to this provider. After making your changes, click **Save** to apply
|
|
93
|
+
them.
|
|
90
94
|
|
|
91
95
|
:::caution
|
|
92
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).
|
|
@@ -76,11 +76,11 @@ usage limits still apply.
|
|
|
76
76
|
|
|
77
77
|
Configure multiple LLM providers within a single AI Gateway project. Supported
|
|
78
78
|
providers include OpenAI, Anthropic, Google, Mistral, xAI, Amazon Bedrock
|
|
79
|
-
(through [Bedrock Mantle](./bedrock-mantle.mdx)),
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
providers.
|
|
79
|
+
(through [Bedrock Mantle](./bedrock-mantle.mdx)), Google Cloud
|
|
80
|
+
([Vertex AI](./vertex-ai.mdx)), and OpenAI-compatible custom providers. See
|
|
81
|
+
[AI Providers](./providers.mdx) for the full list of providers and supported
|
|
82
|
+
capabilities. Apps reference models as `providerName/model`—for example
|
|
83
|
+
`openai/gpt-5-mini`—so a single app can use models from several providers.
|
|
84
84
|
|
|
85
85
|
### Source-Controlled Gateway
|
|
86
86
|
|
|
@@ -3,8 +3,8 @@ title: AI Providers
|
|
|
3
3
|
sidebar_label: Overview
|
|
4
4
|
description:
|
|
5
5
|
The AI providers and capabilities the Zuplo AI Gateway supports, including
|
|
6
|
-
OpenAI, Anthropic, Google, Mistral, xAI, and OpenAI-compatible
|
|
7
|
-
providers.
|
|
6
|
+
OpenAI, Anthropic, Google, Vertex AI, Mistral, xAI, and OpenAI-compatible
|
|
7
|
+
custom providers.
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
Zuplo's AI Gateway supports integration with various AI providers, allowing you
|
|
@@ -21,6 +21,8 @@ Zuplo currently supports the following AI providers:
|
|
|
21
21
|
- xAI (Grok)
|
|
22
22
|
- [Bedrock Mantle](./bedrock-mantle.mdx)—Amazon Bedrock's compatible-APIs
|
|
23
23
|
endpoint, serving Claude models and models from many other vendors
|
|
24
|
+
- [Vertex AI](./vertex-ai.mdx)—Google Cloud's managed model platform, serving
|
|
25
|
+
Gemini and Model Garden models on your own Google Cloud project
|
|
24
26
|
- [Zuplo Demo](#zuplo-demo)—a free, keyless provider for trying the gateway
|
|
25
27
|
- OpenAI-compatible [Custom Providers](./custom-providers.mdx) (such as Qwen,
|
|
26
28
|
Kimi, etc)
|
|
@@ -35,6 +37,7 @@ The following capabilities are supported across providers:
|
|
|
35
37
|
| Mistral | ✅ | ✅ | ❌ | ❌ |
|
|
36
38
|
| xAI | ✅ | ✅ | ❌ | ❌ |
|
|
37
39
|
| Bedrock Mantle | ✅ | ❌ | ✅ | ✅ |
|
|
40
|
+
| Vertex AI | ✅ | ✅ | ❌ | ❌ |
|
|
38
41
|
| Zuplo Demo | ✅ | ❌ | ❌ | ❌ |
|
|
39
42
|
| OpenAI-compatible (Custom) | ✅ | ✅ | ❌ | ❌ |
|
|
40
43
|
|
|
@@ -77,6 +80,32 @@ instead of an endpoint URL, and it accepts only long-term Bedrock API keys,
|
|
|
77
80
|
which start with `ABSK`. For prerequisites, setup steps, code examples, and
|
|
78
81
|
troubleshooting, see [Using Bedrock Mantle](./bedrock-mantle.mdx).
|
|
79
82
|
|
|
83
|
+
## Vertex AI
|
|
84
|
+
|
|
85
|
+
**Vertex AI** is Google Cloud's managed model platform. One provider
|
|
86
|
+
configuration serves both the Gemini family and Vertex's Model Garden partner
|
|
87
|
+
models—DeepSeek, Qwen, GLM, Kimi, MiniMax, Gemma, and GPT-OSS—from your own
|
|
88
|
+
Google Cloud project, on your Google Cloud billing.
|
|
89
|
+
|
|
90
|
+
Two things make its setup different from every other provider:
|
|
91
|
+
|
|
92
|
+
- **It authenticates with a service account, not an API key.** Vertex's
|
|
93
|
+
prediction endpoints reject API keys, so the dialog asks for a service account
|
|
94
|
+
JSON key file and the gateway exchanges it for short-lived access tokens.
|
|
95
|
+
- **It needs a Google Cloud project as well as a location.** Vertex endpoints
|
|
96
|
+
are per-location, and the project isn't part of the endpoint, so the dialog
|
|
97
|
+
asks for both.
|
|
98
|
+
|
|
99
|
+
Every Vertex model ID is publisher-qualified, so a model reference has two
|
|
100
|
+
slashes: a provider named `vertexai` serves `vertexai/google/gemini-3.7-flash`
|
|
101
|
+
and `vertexai/qwen/qwen3-coder-480b-a35b-instruct-maas`. Model availability
|
|
102
|
+
varies by location—most Model Garden models are served only from the `global`
|
|
103
|
+
location. Vertex also hosts Anthropic's Claude models, but on a different API
|
|
104
|
+
surface that the gateway doesn't route to.
|
|
105
|
+
|
|
106
|
+
For prerequisites, Google Cloud setup, provider steps, code examples, and
|
|
107
|
+
troubleshooting, see [Using Vertex AI](./vertex-ai.mdx).
|
|
108
|
+
|
|
80
109
|
## Zuplo Demo
|
|
81
110
|
|
|
82
111
|
**Zuplo Demo** is a free provider that Zuplo operates so you can try the AI
|
|
@@ -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
|
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Using Vertex AI
|
|
3
|
+
sidebar_label: Vertex AI
|
|
4
|
+
description:
|
|
5
|
+
Serve Gemini and Model Garden models from your own Google Cloud project
|
|
6
|
+
through one provider configuration—one Vertex AI location, one project ID, and
|
|
7
|
+
one service account key. The gateway mints and refreshes the access tokens
|
|
8
|
+
Vertex requires.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
**Vertex AI** is Google Cloud's managed model platform. Adding it as a provider
|
|
12
|
+
serves both the Gemini family and Vertex's Model Garden partner models—DeepSeek,
|
|
13
|
+
Qwen, GLM, Kimi, MiniMax, Gemma, and GPT-OSS—to your [apps](./apps.mdx) through
|
|
14
|
+
the [Universal API](./universal-api.mdx), running on your Google Cloud project
|
|
15
|
+
and your Google Cloud billing.
|
|
16
|
+
|
|
17
|
+
Chat completions go to Vertex's **OpenAI-compatible endpoint**, so the gateway
|
|
18
|
+
forwards OpenAI-shaped requests without translating them. Embeddings work too,
|
|
19
|
+
though Vertex has no OpenAI-compatible embeddings endpoint—the gateway
|
|
20
|
+
translates those onto Vertex's own embedding APIs, which is invisible from your
|
|
21
|
+
app. What sets this provider apart is how you connect it: the endpoint is
|
|
22
|
+
per-location, the Google Cloud project is a separate setting, and the credential
|
|
23
|
+
is a **service account key file**—not an API key.
|
|
24
|
+
|
|
25
|
+
:::caution{title="Vertex AI doesn't accept API keys"}
|
|
26
|
+
|
|
27
|
+
Every other AI Gateway provider takes an API key. Vertex doesn't: its prediction
|
|
28
|
+
endpoints reject API keys outright, answering that they expect an OAuth2 access
|
|
29
|
+
token instead. The provider dialog asks for the JSON key file of a Google Cloud
|
|
30
|
+
service account, and the gateway exchanges it for short-lived access tokens on
|
|
31
|
+
your behalf. Pasting an API key into the field fails validation before the
|
|
32
|
+
provider is saved.
|
|
33
|
+
|
|
34
|
+
:::
|
|
35
|
+
|
|
36
|
+
## Model references include the publisher prefix
|
|
37
|
+
|
|
38
|
+
Apps reference a provider's models as `providerName/model`. Every Vertex model
|
|
39
|
+
ID is publisher-qualified, so a Vertex model reference has two slashes in it:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
vertexai/google/gemini-3.7-flash
|
|
43
|
+
└──┬───┘ └──────────┬──────────┘
|
|
44
|
+
provider model
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Everything before the **first** slash is the provider name you chose, and
|
|
48
|
+
everything after it is the model ID exactly as Vertex expects it. The publisher
|
|
49
|
+
isn't always `google`—Model Garden models carry their own vendor's prefix. A
|
|
50
|
+
provider named `vertexai` serves all of these:
|
|
51
|
+
|
|
52
|
+
- `vertexai/google/gemini-3.7-flash`
|
|
53
|
+
- `vertexai/qwen/qwen3-coder-480b-a35b-instruct-maas`
|
|
54
|
+
- `vertexai/deepseek-ai/deepseek-v3.2-maas`
|
|
55
|
+
- `vertexai/zai-org/glm-5-maas`
|
|
56
|
+
|
|
57
|
+
Copy model IDs from the model picker rather than typing them—the `-maas` suffix
|
|
58
|
+
and the publisher prefix are both part of the ID.
|
|
59
|
+
|
|
60
|
+
## How the gateway calls Vertex AI
|
|
61
|
+
|
|
62
|
+
When no valid cached token exists, the gateway uses the service account key to
|
|
63
|
+
request a short-lived Google OAuth 2.0 access token. It caches and refreshes
|
|
64
|
+
that token, then calls Vertex's OpenAI-compatible endpoint with the token as the
|
|
65
|
+
bearer credential.
|
|
66
|
+
|
|
67
|
+
<Diagram height="h-64">
|
|
68
|
+
<DiagramNode id="app">Your app</DiagramNode>
|
|
69
|
+
<DiagramNode id="gateway" variant="zuplo">
|
|
70
|
+
AI Gateway
|
|
71
|
+
</DiagramNode>
|
|
72
|
+
<DiagramNode id="oauth" variant="orange">
|
|
73
|
+
Google OAuth
|
|
74
|
+
</DiagramNode>
|
|
75
|
+
<DiagramGroup id="gcp" label="Your Google Cloud project">
|
|
76
|
+
<DiagramNode id="compat" variant="blue">
|
|
77
|
+
OpenAI-compatible endpoint
|
|
78
|
+
</DiagramNode>
|
|
79
|
+
<DiagramNode id="embed" variant="green">
|
|
80
|
+
Vertex embedding APIs
|
|
81
|
+
</DiagramNode>
|
|
82
|
+
</DiagramGroup>
|
|
83
|
+
<DiagramEdge from="app" to="gateway" label="Universal API" />
|
|
84
|
+
<DiagramEdge
|
|
85
|
+
from="gateway"
|
|
86
|
+
to="oauth"
|
|
87
|
+
label="service account key → access token"
|
|
88
|
+
lineStyle="dashed"
|
|
89
|
+
/>
|
|
90
|
+
<DiagramEdge from="gateway" to="compat" label="chat completions" />
|
|
91
|
+
<DiagramEdge from="gateway" to="embed" label="embeddings" />
|
|
92
|
+
</Diagram>
|
|
93
|
+
|
|
94
|
+
The request URL the gateway builds combines both settings you enter:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
https://us-central1-aiplatform.googleapis.com
|
|
98
|
+
/v1/projects/my-project-123/locations/us-central1/endpoints/openapi/chat/completions
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The **location** lives in the hostname, which is why the provider dialog asks
|
|
102
|
+
for a location instead of an endpoint URL. The **project** doesn't appear in the
|
|
103
|
+
hostname at all—it appears only in the path—so it can't be derived from the
|
|
104
|
+
endpoint and is a field of its own. That's why the project is required rather
|
|
105
|
+
than optional: without it the gateway would have to guess, and a guess would
|
|
106
|
+
send your traffic and your spend to someone else's Google Cloud project.
|
|
107
|
+
|
|
108
|
+
## Supported endpoints
|
|
109
|
+
|
|
110
|
+
| Endpoint | Vertex AI models |
|
|
111
|
+
| ---------------------- | ---------------- |
|
|
112
|
+
| `/v1/chat/completions` | ✅ Forwarded |
|
|
113
|
+
| `/v1/embeddings` | ✅ Translated |
|
|
114
|
+
| `/v1/responses` | ❌ |
|
|
115
|
+
| `/v1/messages` | ❌ |
|
|
116
|
+
|
|
117
|
+
Streaming (`stream: true`) works on chat completions.
|
|
118
|
+
|
|
119
|
+
The gateway rejects `/v1/responses` and `/v1/messages` requests for Vertex
|
|
120
|
+
models with a `400` error before any upstream call, because Vertex serves
|
|
121
|
+
neither the OpenAI Responses API nor the Anthropic Messages API.
|
|
122
|
+
|
|
123
|
+
:::note{title="Anthropic models on Vertex aren't supported"}
|
|
124
|
+
|
|
125
|
+
Vertex also hosts Anthropic's Claude models, but on a different API surface than
|
|
126
|
+
the one this provider uses. The gateway can't route to them. To use Claude
|
|
127
|
+
models, add the [Anthropic](./providers.mdx) provider or
|
|
128
|
+
[Bedrock Mantle](./bedrock-mantle.mdx).
|
|
129
|
+
|
|
130
|
+
:::
|
|
131
|
+
|
|
132
|
+
## Model availability depends on the location
|
|
133
|
+
|
|
134
|
+
Google registers each Vertex model in specific locations, and it changes that
|
|
135
|
+
list over time. Most Model Garden models are served only from the **global**
|
|
136
|
+
location; the Gemini models are more widely available.
|
|
137
|
+
|
|
138
|
+
The model picker doesn't filter by your provider's location, so every model
|
|
139
|
+
stays selectable from every provider. If you call a model that isn't served
|
|
140
|
+
where your provider points, Vertex answers with a `400` that names both:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
Publisher model 'publishers/qwen/models/qwen3-coder-480b-a35b-instruct-maas'
|
|
144
|
+
is not available in region 'us-central1'
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Set the provider's **Location** to `global` if you plan to use Model Garden
|
|
148
|
+
models, or add a second Vertex provider for a specific region and route to
|
|
149
|
+
whichever one serves the model you want.
|
|
150
|
+
|
|
151
|
+
## Before you begin
|
|
152
|
+
|
|
153
|
+
You need:
|
|
154
|
+
|
|
155
|
+
- A Google Cloud project with the **Vertex AI API**
|
|
156
|
+
(`aiplatform.googleapis.com`) enabled and billing enabled. Gemini models
|
|
157
|
+
require both.
|
|
158
|
+
- A service account in that project holding the **Vertex AI User**
|
|
159
|
+
(`roles/aiplatform.user`) role, and a **JSON key** for it. The steps are in
|
|
160
|
+
[Set up Google Cloud](#set-up-google-cloud).
|
|
161
|
+
- The project's **project ID**—the readable identifier such as `my-project-123`,
|
|
162
|
+
not the numeric project number.
|
|
163
|
+
- An AI Gateway project in the Zuplo Portal.
|
|
164
|
+
- An AI Gateway [app](./apps.mdx) to call the models from. The app page shows
|
|
165
|
+
the app's API URL, and its API key lives on the app's **API Key** tab.
|
|
166
|
+
|
|
167
|
+
## Set up Google Cloud
|
|
168
|
+
|
|
169
|
+
Do this before adding the provider—the key file you download in the last step is
|
|
170
|
+
what the provider dialog asks for.
|
|
171
|
+
|
|
172
|
+
<Stepper>
|
|
173
|
+
|
|
174
|
+
1. In the [Google Cloud console](https://console.cloud.google.com/), select the
|
|
175
|
+
project you want to serve models from and note its **project ID**. The
|
|
176
|
+
console shows the ID next to the project name, alongside the project number;
|
|
177
|
+
the gateway needs the ID.
|
|
178
|
+
|
|
179
|
+
1. Enable the **Vertex AI API** on the project, under **APIs & Services →
|
|
180
|
+
Library**. Confirm billing is enabled for the project as well.
|
|
181
|
+
|
|
182
|
+
1. Create a service account under **IAM & Admin → Service Accounts**.
|
|
183
|
+
|
|
184
|
+
1. Grant that service account the **Vertex AI User** (`roles/aiplatform.user`)
|
|
185
|
+
role **on the same project**. This is the smallest role that can call
|
|
186
|
+
prediction endpoints. Vertex AI Administrator grants more than the gateway
|
|
187
|
+
needs, and Vertex AI Viewer can't make predictions at all.
|
|
188
|
+
|
|
189
|
+
1. Create a JSON key for the service account: open the service account, go to
|
|
190
|
+
**Keys → Add key → Create new key**, choose **JSON**, and download the file.
|
|
191
|
+
Keep it somewhere safe—Google doesn't let you download it again.
|
|
192
|
+
|
|
193
|
+
</Stepper>
|
|
194
|
+
|
|
195
|
+
:::note
|
|
196
|
+
|
|
197
|
+
Enabling the API and granting the role are separate steps, and missing the first
|
|
198
|
+
one is the most common setup failure. It surfaces as a `403` that talks about
|
|
199
|
+
the Vertex AI API rather than about permissions, which reads like a bad key.
|
|
200
|
+
|
|
201
|
+
:::
|
|
202
|
+
|
|
203
|
+
## Add the provider
|
|
204
|
+
|
|
205
|
+
Adding or editing providers requires the **Edit** permission, granted to Zuplo
|
|
206
|
+
account and project **Admins**—see
|
|
207
|
+
[Managing Providers](./managing-providers.mdx).
|
|
208
|
+
|
|
209
|
+
<Stepper>
|
|
210
|
+
|
|
211
|
+
1. Open
|
|
212
|
+
[**Settings → AI Providers**](https://portal.zuplo.com/+/account/project/ai/settings/data-models)
|
|
213
|
+
in your AI Gateway project in the Zuplo Portal.
|
|
214
|
+
|
|
215
|
+
1. Click the **Add Provider** button.
|
|
216
|
+
|
|
217
|
+
1. In the **AI Provider** list, select **Vertex AI** from the Default Providers
|
|
218
|
+
group.
|
|
219
|
+
|
|
220
|
+
1. Review the **Provider Name**, which fills in as `vertexai` (a second
|
|
221
|
+
configuration becomes `vertexai-2`). You can replace it with your own name,
|
|
222
|
+
but only now—the name is permanent after creation, and it's the prefix in
|
|
223
|
+
every model reference: a provider named `vertexai` serves
|
|
224
|
+
`vertexai/google/gemini-3.7-flash`.
|
|
225
|
+
|
|
226
|
+
1. In **Location**, enter the Vertex AI location to call, such as `us-central1`
|
|
227
|
+
or `europe-west4`. Enter `global` to use Vertex's global endpoint. The
|
|
228
|
+
gateway derives the endpoint from this value—there's no URL to enter.
|
|
229
|
+
|
|
230
|
+
1. In **Google Cloud Project ID**, enter the project ID you noted earlier, such
|
|
231
|
+
as `my-project-123`. The dialog rejects an all-digits value, which is the
|
|
232
|
+
project number rather than the ID.
|
|
233
|
+
|
|
234
|
+
1. In **Service Account Key (JSON)**, paste the entire contents of the JSON key
|
|
235
|
+
file you downloaded. Paste it whole—a truncated copy fails validation.
|
|
236
|
+
|
|
237
|
+
1. Select the models to enable, or click **Select All**. The picker shows
|
|
238
|
+
Google's per-token prices, which the gateway uses to track cost per app. You
|
|
239
|
+
can change the selection later.
|
|
240
|
+
|
|
241
|
+
1. Click **Create**.
|
|
242
|
+
|
|
243
|
+
</Stepper>
|
|
244
|
+
|
|
245
|
+
:::note
|
|
246
|
+
|
|
247
|
+
Saving provider settings triggers an automatic production deployment of your
|
|
248
|
+
gateway, because provider credentials are part of the deployed gateway. The
|
|
249
|
+
change is live once the deployment completes.
|
|
250
|
+
|
|
251
|
+
:::
|
|
252
|
+
|
|
253
|
+
:::note{title="Prices in the picker are a snapshot"}
|
|
254
|
+
|
|
255
|
+
The per-token prices shown in the model picker are recorded rates, not a live
|
|
256
|
+
read of Google's price list, and some Gemini models are on promotional rates
|
|
257
|
+
that Google marks as temporary. Treat the gateway's cost figures as an estimate
|
|
258
|
+
and your Google Cloud bill as the record.
|
|
259
|
+
|
|
260
|
+
:::
|
|
261
|
+
|
|
262
|
+
When you edit the provider later—see
|
|
263
|
+
[Managing Providers](./managing-providers.mdx)—the **Location** and **Google
|
|
264
|
+
Cloud Project ID** fields show the saved values and stay editable, and you can
|
|
265
|
+
replace the key file. Leave the key field blank to keep the stored key. The
|
|
266
|
+
**Provider Name** doesn't change.
|
|
267
|
+
|
|
268
|
+
## Verify the provider
|
|
269
|
+
|
|
270
|
+
Once the deployment completes, send a chat completions request to your app's
|
|
271
|
+
[Universal API](./universal-api.mdx) URL—shown at the top of the
|
|
272
|
+
[app page](./apps.mdx)—with the app's API key as the bearer token and one of the
|
|
273
|
+
models you enabled:
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
curl https://my-gateway-main-2e18f50.zuplo.app/config_fe0a04972d2848e0a94ae4b8bcd1497e/v1/chat/completions \
|
|
277
|
+
-H "Authorization: Bearer $ZUPLO_APP_API_KEY" \
|
|
278
|
+
-H "Content-Type: application/json" \
|
|
279
|
+
-d '{
|
|
280
|
+
"model": "vertexai/google/gemini-3.7-flash",
|
|
281
|
+
"messages": [{ "role": "user", "content": "Say hi" }]
|
|
282
|
+
}'
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The URL is a sample—replace it with your app's API URL plus
|
|
286
|
+
`/v1/chat/completions`, set `ZUPLO_APP_API_KEY` to the app's API key, and
|
|
287
|
+
substitute any model you enabled for `vertexai/google/gemini-3.7-flash`.
|
|
288
|
+
|
|
289
|
+
A `200` response confirms the provider works; the body carries
|
|
290
|
+
`"provider": "vertex-ai"`—the provider type, not the name you chose. If the
|
|
291
|
+
request fails immediately after you save the provider, the deployment may not
|
|
292
|
+
have finished—retry before debugging further.
|
|
293
|
+
|
|
294
|
+
## Call the models
|
|
295
|
+
|
|
296
|
+
Use any OpenAI client with your app's URL plus `/v1` as the base URL and the
|
|
297
|
+
app's API key:
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
import OpenAI from "openai";
|
|
301
|
+
|
|
302
|
+
const client = new OpenAI({
|
|
303
|
+
apiKey: process.env.ZUPLO_APP_API_KEY,
|
|
304
|
+
baseURL:
|
|
305
|
+
"https://my-gateway-main-2e18f50.zuplo.app/config_fe0a04972d2848e0a94ae4b8bcd1497e/v1",
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const response = await client.chat.completions.create({
|
|
309
|
+
model: "vertexai/google/gemini-3.7-flash",
|
|
310
|
+
messages: [{ role: "user", content: "Summarize this ticket." }],
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Streaming works the same way—set `stream: true` and read the
|
|
315
|
+
`chat.completion.chunk` events.
|
|
316
|
+
|
|
317
|
+
### Embeddings
|
|
318
|
+
|
|
319
|
+
Embeddings use the same client and the same `/v1/embeddings` endpoint as any
|
|
320
|
+
other provider:
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
const embeddings = await client.embeddings.create({
|
|
324
|
+
model: "vertexai/google/gemini-embedding-2",
|
|
325
|
+
input: ["first document", "second document"],
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
`dimensions` and `encoding_format` both work. Two Vertex-specific limits apply,
|
|
330
|
+
and the gateway enforces both before calling Vertex, so you get a `400` rather
|
|
331
|
+
than a partial charge:
|
|
332
|
+
|
|
333
|
+
- **At most 250 inputs per request.** Split larger batches yourself.
|
|
334
|
+
- **Text input only.** Pre-tokenized input—arrays of token IDs, which the OpenAI
|
|
335
|
+
API also accepts—is rejected, because Vertex has no way to represent it. Send
|
|
336
|
+
the original text.
|
|
337
|
+
|
|
338
|
+
## Troubleshooting
|
|
339
|
+
|
|
340
|
+
**The dialog rejects your credential.** Vertex uses a service account, not an
|
|
341
|
+
API key. The field takes the whole JSON key file downloaded from **IAM & Admin →
|
|
342
|
+
Service Accounts → Keys**. The dialog also rejects a workload identity
|
|
343
|
+
federation configuration—its `type` is `external_account` rather than
|
|
344
|
+
`service_account`—and a file whose `private_key` isn't a PEM-encoded key. Paste
|
|
345
|
+
the file exactly as downloaded, keeping the `\n` escapes intact.
|
|
346
|
+
|
|
347
|
+
**The dialog rejects your project ID.** Project IDs are 6 to 30 characters:
|
|
348
|
+
lowercase letters, digits, and hyphens, starting with a letter and not ending in
|
|
349
|
+
one. An all-digits value is the project number, which the Google Cloud console
|
|
350
|
+
shows next to the ID; the gateway needs the ID.
|
|
351
|
+
|
|
352
|
+
**A request fails with a `403` about the Vertex AI API.** The API isn't enabled
|
|
353
|
+
on the project. Enable **Vertex AI API** under **APIs & Services → Library** and
|
|
354
|
+
confirm the project has billing enabled. This error names the API rather than
|
|
355
|
+
permissions, so it's easy to mistake for a credential problem.
|
|
356
|
+
|
|
357
|
+
**A request fails with a permission error.** The service account is missing the
|
|
358
|
+
**Vertex AI User** (`roles/aiplatform.user`) role, or holds it on a different
|
|
359
|
+
project than the one configured on the provider. Grant the role on the same
|
|
360
|
+
project you entered as the **Google Cloud Project ID**.
|
|
361
|
+
|
|
362
|
+
**A request fails with `400` saying the model isn't available in the region.**
|
|
363
|
+
The model isn't served from your provider's location. Most Model Garden models
|
|
364
|
+
are global-only—see
|
|
365
|
+
[Model availability](#model-availability-depends-on-the-location). Set the
|
|
366
|
+
provider's **Location** to `global`, or use a provider that points at a location
|
|
367
|
+
serving that model.
|
|
368
|
+
|
|
369
|
+
**An embeddings request fails with `400` about the input.** The gateway accepts
|
|
370
|
+
at most 250 inputs per request and text input only. See
|
|
371
|
+
[Embeddings](#embeddings).
|
|
372
|
+
|
|
373
|
+
**A `/v1/responses` or `/v1/messages` request fails with `400`.** Vertex's
|
|
374
|
+
OpenAI-compatible endpoint serves neither API. Use `/v1/chat/completions`, or
|
|
375
|
+
another provider for those endpoints—see
|
|
376
|
+
[Supported endpoints](#supported-endpoints).
|
|
377
|
+
|
|
378
|
+
## Next steps
|
|
379
|
+
|
|
380
|
+
- [AI Providers](./providers.mdx)—the capability matrix across every supported
|
|
381
|
+
provider.
|
|
382
|
+
- [Universal API](./universal-api.mdx)—the endpoints every app serves and how
|
|
383
|
+
model references work.
|
|
384
|
+
- [Managing Providers](./managing-providers.mdx)—edit models, keys, the
|
|
385
|
+
location, and the project, and understand when changes deploy.
|
|
386
|
+
- [AI Gateway Apps](./apps.mdx)—create the apps that call your Vertex-backed
|
|
387
|
+
models.
|
|
388
|
+
- [Model Filtering policy](../policies/ai-gateway-model-filtering-v2-inbound.mdx)—control
|
|
389
|
+
which models each app can call.
|
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
|
}
|