zuplo 7.7.9 → 7.7.10

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.
@@ -111,19 +111,53 @@ resource serves Claude on either host family the provider accepts.
111
111
  | ---------------------- | ------------------------ | ------------- |
112
112
  | `/v1/chat/completions` | ✅ Forwarded | ✅ Translated |
113
113
  | `/v1/embeddings` | ✅ Embedding models | ❌ |
114
- | `/v1/messages` | ❌ | ✅ Native |
115
- | `/v1/responses` | | ❌ |
114
+ | `/v1/messages` | ❌ | ✅ Forwarded |
115
+ | `/v1/responses` | ✅ Models that serve it | ❌ |
116
116
 
117
- Streaming (`stream: true`) works on chat completions and messages.
117
+ **Forwarded** means the gateway sends your request on in the shape you wrote it.
118
+ **Translated** means it converts between two API shapes, which limits you to the
119
+ parameters listed below.
120
+
121
+ Streaming (`stream: true`) works on chat completions, messages, and responses.
118
122
 
119
123
  The catalog carries three embedding models—`text-embedding-3-small`,
120
124
  `text-embedding-3-large` and `text-embedding-ada-002`—so `/v1/embeddings` works
121
125
  once you deploy one of them and map your deployment to it.
122
126
 
123
- `/v1/responses` isn't served for Azure AI, and a Responses request fails with a
124
- `400` error before any upstream call. Azure does serve a Responses API on its
125
- own endpoint; the gateway doesn't route to it yet. Use `/v1/chat/completions`
126
- instead.
127
+ `/v1/responses` serves the OpenAI Responses API, buffered and streaming. Support
128
+ is **per deployed model**, not per resource: Azure serves Responses for
129
+ particular model and version combinations, and a deployment outside that set
130
+ answers `400` with `The requested operation is unsupported`—which is what an
131
+ embedding deployment on this endpoint returns. Check Azure's own Responses API
132
+ model list for your model and version before relying on it.
133
+
134
+ The management operations on a stored response are forwarded too:
135
+
136
+ | Operation | Does |
137
+ | ------------------------------------ | ------------------------------------- |
138
+ | `GET /v1/responses/{id}` | Retrieves the response |
139
+ | `GET /v1/responses/{id}/input_items` | Lists the input items of one response |
140
+ | `DELETE /v1/responses/{id}` | Deletes the response |
141
+
142
+ Two things they need. Send **`store: true`** on the create, since your resource
143
+ serves them only for a response it kept. And because these requests carry no
144
+ body, there is no `model` in them for the gateway to route on—it uses the app's
145
+ first configured completions model instead, so the app needs a
146
+ [Model Filtering](../policies/ai-gateway-model-filtering-v2-inbound.mdx) policy
147
+ whose completions allow list **starts with an OpenAI-compatible Azure
148
+ deployment**. Without one the gateway answers `400`, asking for preselected
149
+ routing, before reaching Azure.
150
+
151
+ A Claude deployment doesn't work in that first slot, even though the allow list
152
+ accepts it—Claude doesn't serve Responses at all, so the request still fails.
153
+
154
+ Azure also takes a moment to make a new response readable. A `GET` immediately
155
+ after a create can return `404` before it settles, so retry rather than treating
156
+ the first `404` as final.
157
+
158
+ Claude deployments don't serve Responses, so a Claude model on this endpoint
159
+ fails with a `400` error—use
160
+ [`/v1/messages`](#call-claude-models-on-the-messages-api) for those.
127
161
 
128
162
  Claude models work on `/v1/chat/completions` through the gateway's translation
129
163
  to the Messages API, which supports the core chat parameters: `messages`,
@@ -334,20 +368,27 @@ different beta still gets an answer, without that feature.
334
368
  The `model` field in a response doesn't always say what you might expect, and
335
369
  what it says depends on the model you called.
336
370
 
337
- **Claude responses report your deployment name.** Every other response reports
338
- Azure's own model ID, which usually carries a version date the short name
339
- doesn't.
371
+ **Claude models and `/v1/responses` report your deployment name.** Everything
372
+ else reports Azure's own model ID, which usually carries a version date the
373
+ short name doesn't.
340
374
 
341
375
  | Endpoint | Deployment `my-gpt` / `my-embed` / `my-claude` serves | `model` in the response |
342
376
  | ---------------------- | ----------------------------------------------------- | ------------------------- |
343
377
  | `/v1/chat/completions` | `gpt-4.1-mini` | `gpt-4.1-mini-2025-04-14` |
378
+ | `/v1/responses` | `gpt-4.1-mini` | `my-gpt` |
344
379
  | `/v1/embeddings` | `text-embedding-3-small` | `text-embedding-3-small` |
345
380
  | `/v1/messages` | `claude-haiku-4-5` | `my-claude` |
346
381
  | `/v1/chat/completions` | `claude-haiku-4-5` | `my-claude` |
347
382
 
383
+ The same deployment reports two different values depending on the endpoint you
384
+ call it on: chat completions answers with Azure's dated ID, Responses with the
385
+ name you gave the deployment. That's Azure's own behavior on each surface, not
386
+ something the gateway changes.
387
+
348
388
  A Claude model reports your deployment name on both endpoints—the native
349
- Messages API and the chat completions translation—because the gateway builds
350
- those responses itself and echoes back the model you asked for.
389
+ Messages API and the chat completions translation—but for a different reason:
390
+ there the gateway builds the response itself and echoes back the model you asked
391
+ for.
351
392
 
352
393
  One exception, if you stream Claude: the `message_start` event reports Azure's
353
394
  ID (`claude-haiku-4-5-20251001`) rather than your deployment name, because the
@@ -388,8 +429,23 @@ resources deploy Claude. Confirm the deployment exists and is Anthropic-format,
388
429
  and see [Deploying Claude on Foundry](#deploying-claude-on-foundry) for the
389
430
  requirements Azure applies to those deployments.
390
431
 
391
- **A `400` error names `/v1/responses`.** The gateway doesn't serve the Responses
392
- API for Azure AI. Use `/v1/chat/completions`.
432
+ **A `400` error names `/v1/responses`.** Two different causes, and the message
433
+ tells you which. If it names the provider or the endpoint, the deployment is a
434
+ **Claude** one—Claude doesn't serve Responses, so call
435
+ [`/v1/messages`](#call-claude-models-on-the-messages-api) instead, or
436
+ `/v1/chat/completions` for the translated form. If it says
437
+ `The requested operation is unsupported`, that came from Azure: the deployed
438
+ model doesn't serve Responses. Support is per model and version, so check
439
+ Azure's Responses API model list—an embedding deployment always fails this way.
440
+
441
+ **A `400` asks for preselected routing.** You called `GET` or `DELETE` on a
442
+ response, or listed its input items. Those requests carry no body, so there is
443
+ no `model` for the gateway to route on and it falls back to the app's first
444
+ configured completions model. Add a
445
+ [Model Filtering](../policies/ai-gateway-model-filtering-v2-inbound.mdx) policy
446
+ to the app with an **OpenAI-compatible** Azure deployment first in its
447
+ completions allow list. A Claude deployment in that slot still fails, since
448
+ Claude doesn't serve Responses.
393
449
 
394
450
  **A model in the picker fails at request time.** The picker lists the full Azure
395
451
  catalog, but a model only works once you deploy it in your resource, and model
@@ -72,9 +72,13 @@ either way.
72
72
  | ---------------------- | ---------------------------- | ------------- |
73
73
  | `/v1/chat/completions` | ✅ Forwarded | ✅ Translated |
74
74
  | `/v1/responses` | ✅ Models that serve it | ❌ |
75
- | `/v1/messages` | ❌ | ✅ Native |
75
+ | `/v1/messages` | ❌ | ✅ Forwarded |
76
76
  | `/v1/embeddings` | ❌ Mantle has no such models | ❌ |
77
77
 
78
+ **Forwarded** means the gateway sends your request on in the shape you wrote it.
79
+ **Translated** means it converts between two API shapes, which limits you to the
80
+ parameters listed below.
81
+
78
82
  Streaming (`stream: true`) works on all three serving endpoints.
79
83
 
80
84
  On the OpenAI-compatible side, endpoint support is per model, decided by AWS:
@@ -10,6 +10,17 @@ You can configure [Claude Code](https://www.claude.com/product/claude-code) to
10
10
  send its model requests through the Zuplo AI Gateway, so the gateway
11
11
  authenticates, meters, and routes every Claude Code session.
12
12
 
13
+ The gateway can authenticate to Anthropic in two ways. Pick one before you
14
+ configure Claude Code:
15
+
16
+ | Approach | The gateway sends Anthropic | Claude Code sends the gateway |
17
+ | --------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------- |
18
+ | [Configured provider key](#use-a-configured-provider-key) | The Anthropic API key saved on your provider | The app's API key as `ANTHROPIC_AUTH_TOKEN` |
19
+ | [Your Claude subscription](#use-your-claude-subscription) | The credential from your `claude.ai` login, such as a Claude Max plan | The app's API key in a `zp-gateway-api-key` header, plus your login as-is |
20
+
21
+ Both approaches share the same provider, team, and app setup, and the same model
22
+ configuration.
23
+
13
24
  ## Claude Code Setup
14
25
 
15
26
  <Stepper>
@@ -19,12 +30,18 @@ authenticates, meters, and routes every Claude Code session.
19
30
 
20
31
  2. [Set up a new team](../managing-teams.mdx)
21
32
 
22
- 3. Create a [new app](../managing-apps.mdx) to use with Claude Code and assign
33
+ 3. If you plan to use your Claude subscription, configure the team's
34
+ [API Key Authentication policy](#configure-the-api-key-authentication-policy)
35
+ for passthrough before you create the app
36
+
37
+ 4. Create a [new app](../managing-apps.mdx) to use with Claude Code and assign
23
38
  it to the team you created
24
39
 
25
- 4. Copy the **API URL** and **API Key** shown at the top of the app page
40
+ 5. Copy the **API URL** and **API Key** shown at the top of the app page
26
41
 
27
- 5. Add the API key, gateway URL, and model mappings to `.claude/settings.json`
42
+ 6. Add the gateway URL, the app's API key, and model mappings to
43
+ `.claude/settings.json`, following the section below for the approach you
44
+ chose
28
45
 
29
46
  </Stepper>
30
47
 
@@ -45,7 +62,7 @@ The gateway routes every request using `providerName/model`. The provider prefix
45
62
  picks which of your providers handles the request — a gateway can contain
46
63
  several providers that serve Anthropic models.
47
64
 
48
- ### Using settings.json
65
+ ### Where Claude Code reads its settings
49
66
 
50
67
  You can configure Claude Code either globally or per project by editing the
51
68
  settings file:
@@ -53,6 +70,23 @@ settings file:
53
70
  - **Global:** `~/.claude/settings.json`
54
71
  - **Project-specific:** `.claude/settings.json` in your project directory
55
72
 
73
+ Each approach below shows the `env` block to add to that file. Restart Claude
74
+ Code after saving it, and all your Claude Code LLM requests route through the AI
75
+ Gateway.
76
+
77
+ :::note
78
+
79
+ Claude Code appends `/v1/messages` to the base URL itself, so
80
+ `ANTHROPIC_BASE_URL` is the app's URL _without_ the `/v1` suffix.
81
+
82
+ :::
83
+
84
+ ## Use a configured provider key
85
+
86
+ With this approach the gateway calls Anthropic with the API key you saved on the
87
+ [provider](../managing-providers.mdx). Claude Code presents only the app's API
88
+ key, and Anthropic bills the account that owns the provider key.
89
+
56
90
  Add the following environment configuration to the settings file:
57
91
 
58
92
  ```json
@@ -70,17 +104,138 @@ Add the following environment configuration to the settings file:
70
104
  }
71
105
  ```
72
106
 
73
- :::note
107
+ `ANTHROPIC_AUTH_TOKEN` replaces whatever login Claude Code would otherwise use,
108
+ so this approach doesn't consume your Claude subscription.
74
109
 
75
- Claude Code appends `/v1/messages` to the base URL itself, so
76
- `ANTHROPIC_BASE_URL` is the app's URL _without_ the `/v1` suffix.
110
+ ## Use your Claude subscription
77
111
 
78
- :::
112
+ With this approach Claude Code stays signed in to `claude.ai` — for example with
113
+ a Claude Max plan — and the gateway forwards that login credential to Anthropic
114
+ as the request's provider credential. The app's API key travels in a separate
115
+ `zp-gateway-api-key` header, so the gateway still authenticates the app, applies
116
+ its policy chain, and meters its usage.
117
+
118
+ Two pieces of configuration make this work: the app's **API Key Authentication**
119
+ policy accepts the app key from the `zp-gateway-api-key` header and passes the
120
+ `Authorization` header through, and Claude Code sends both headers.
121
+
122
+ ### Configure the API Key Authentication policy
123
+
124
+ The [API Key Authentication](../../policies/ai-gateway-auth-v2-inbound.mdx)
125
+ policy needs three options for passthrough:
126
+
127
+ | Option | Value | Effect |
128
+ | ----------------------- | -------------------- | -------------------------------------------------------- |
129
+ | `credentialPassthrough` | On | Forward the caller's `Authorization` header to Anthropic |
130
+ | `authHeader` | `zp-gateway-api-key` | Read the app's API key from this header instead |
131
+ | `authScheme` | Empty value | The header holds the bare key, with no `Bearer` prefix |
132
+
133
+ You can set these options on the team's
134
+ [policy template](../policy-templates.mdx), so every app created in the team
135
+ starts with passthrough enabled, or on a single app's **Policies** tab. The
136
+ steps below use the team template; the app's policy editor works the same way.
137
+
138
+ <Stepper>
139
+
140
+ 1. Open the team in the Zuplo Portal and select its **Policy Template** tab.
141
+
142
+ 2. Click the edit (pencil) icon on the **API Key Authentication** entry.
143
+
144
+ 3. Turn on **credentialPassthrough**.
145
+
146
+ 4. Set **authHeader** to `zp-gateway-api-key`.
147
+
148
+ 5. Leave **authScheme** blank and click **Use an empty value for authScheme**.
149
+ The field then reports "Empty value set. The default (Bearer) is not
150
+ applied." Clearing the field alone isn't enough: a blank field omits the
151
+ option, so the `Bearer` default still applies and every request fails with
152
+ `401 Invalid Authorization Scheme`.
153
+
154
+ 6. Click **Apply**, then **Save changes**.
155
+
156
+ </Stepper>
157
+
158
+ The **JSON** editor mode shows the equivalent options:
159
+
160
+ ```json
161
+ {
162
+ "credentialPassthrough": true,
163
+ "authHeader": "zp-gateway-api-key",
164
+ "authScheme": ""
165
+ }
166
+ ```
167
+
168
+ ### The provider still needs an API key
169
+
170
+ The Portal requires an API key to save an Anthropic provider, and passthrough
171
+ requests never send it: the request to Anthropic carries the caller's credential
172
+ instead. Anything else that uses this provider's configured credential still
173
+ needs a real key. That includes apps on this gateway that don't use passthrough
174
+ and any
175
+ [Fallback Model](../../policies/ai-gateway-fallback-model-v2-inbound.mdx)
176
+ target, because fallback requests use the provider's configured credential
177
+ rather than the caller's. If nothing on the gateway uses the configured
178
+ credential, a placeholder value works.
179
+
180
+ The app's playground in the Portal sends the app key as a bearer token in
181
+ `Authorization`, so it can't call an app that uses passthrough. Test the app
182
+ from Claude Code instead, as described below.
183
+
184
+ ### Configure Claude Code
185
+
186
+ Add the following environment configuration to the settings file. Don't set
187
+ `ANTHROPIC_AUTH_TOKEN` or `ANTHROPIC_API_KEY` here or in your shell: either one
188
+ replaces your `claude.ai` login, and the gateway would then forward that value
189
+ to Anthropic as the credential.
190
+
191
+ ```json
192
+ {
193
+ "env": {
194
+ "ANTHROPIC_BASE_URL": "https://my-gateway-main-2e18f50.zuplo.app/config_fe0a04972d2848e0a94ae4b8bcd1497e",
195
+ "ANTHROPIC_CUSTOM_HEADERS": "zp-gateway-api-key: <your-ai-gateway-app-api-key>",
196
+ "ANTHROPIC_MODEL": "anthropic/claude-sonnet-5",
197
+ "ANTHROPIC_SMALL_FAST_MODEL": "anthropic/claude-haiku-4-5",
198
+ "ANTHROPIC_DEFAULT_OPUS_MODEL": "anthropic/claude-opus-5",
199
+ "ANTHROPIC_DEFAULT_SONNET_MODEL": "anthropic/claude-sonnet-5",
200
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL": "anthropic/claude-haiku-4-5",
201
+ "ANTHROPIC_DEFAULT_FABLE_MODEL": "anthropic/claude-fable-5"
202
+ }
203
+ }
204
+ ```
205
+
206
+ `ANTHROPIC_CUSTOM_HEADERS` adds the `zp-gateway-api-key` header to every request
207
+ Claude Code makes, while Claude Code keeps sending its own `Authorization`
208
+ header from your login. The gateway consumes those incoming headers, uses the
209
+ login credential to authenticate the Anthropic request, and never sends your app
210
+ key to Anthropic.
211
+
212
+ ### Verify the subscription setup
213
+
214
+ Restart Claude Code, then confirm which credential it's using and send one
215
+ request through the gateway:
216
+
217
+ ```bash
218
+ claude auth status --text
219
+ claude -p "Reply with the single word OK"
220
+ ```
221
+
222
+ The first command should report that you're signed in with your `claude.ai`
223
+ account rather than an API key or auth token. The second returns a short reply,
224
+ and the request appears in the app's usage in the Zuplo Portal. In an
225
+ interactive session, `/status` shows the same login information.
226
+
227
+ If the request fails:
79
228
 
80
- Restart Claude and it will switch to using your new AI Gateway configuration and
81
- all your Claude Code LLM requests will route through the AI Gateway.
229
+ - `401` with `Invalid Authorization Scheme` `authScheme` still has its
230
+ `Bearer` default. Set it to an explicit empty value in the policy.
231
+ - `401` with `Header configured by options.authHeader is missing` — Claude Code
232
+ didn't send `zp-gateway-api-key`. Check `ANTHROPIC_CUSTOM_HEADERS`.
233
+ - `401` with `credentialPassthrough requires a non-empty Authorization header` —
234
+ Claude Code isn't signed in. Run `claude` and use `/login`.
235
+ - An authentication error from Anthropic — Anthropic rejected the forwarded
236
+ login credential. Sign in to Claude Code again.
82
237
 
83
- ### Configure every Claude Code model variable
238
+ ## Configure every Claude Code model variable
84
239
 
85
240
  The model ids in these examples reflect what was current when this page was
86
241
  written, and ids differ across providers — a Bedrock provider serves different
@@ -38,7 +38,7 @@ The following capabilities are supported across providers:
38
38
  | Google | ✅ | ✅ | ❌ | ❌ |
39
39
  | Mistral | ✅ | ✅ | ❌ | ❌ |
40
40
  | xAI | ✅ | ✅ | ❌ | ❌ |
41
- | Azure AI | ✅ | ✅ | | ✅ |
41
+ | Azure AI | ✅ | ✅ | | ✅ |
42
42
  | Bedrock Mantle | ✅ | ❌ | ✅ | ✅ |
43
43
  | Vertex AI | ✅ | ✅ | ❌ | ✅ |
44
44
  | Zuplo Demo | ✅ | ❌ | ❌ | ❌ |
@@ -54,8 +54,12 @@ Messages (plus chat completions through translation), while its other models
54
54
  serve chat completions and—per model—Responses. See
55
55
  [Using Bedrock Mantle](./bedrock-mantle.mdx#supported-endpoints-by-model-family).
56
56
 
57
- Azure AI's Messages support needs a Microsoft Foundry resource, since Claude
58
- can't be deployed on an Azure OpenAI resource. Azure also addresses models by
57
+ Azure AI's capabilities split by model family, and within the OpenAI-compatible
58
+ family they split per deployed model: a chat model serves chat completions and—
59
+ per model and version—Responses, while an embedding model serves embeddings and
60
+ neither of the others. Claude serves Messages (plus chat completions through
61
+ translation) and needs a Microsoft Foundry resource, since Claude can't be
62
+ deployed on an Azure OpenAI resource. Azure also addresses models by
59
63
  **deployment name** rather than by published model ID, so a deployment named
60
64
  differently from the model it serves needs mapping for the gateway to price it.
61
65
  See [Using Azure AI](./azure-ai.mdx#azure-serves-deployments-not-model-names).
@@ -76,8 +80,8 @@ name you give the provider configuration. See the
76
80
  configuration covers both kinds of resource, and which capabilities apply
77
81
  depends on the resource and the model:
78
82
 
79
- - An **Azure OpenAI** resource deploys OpenAI models and serves chat completions
80
- and embeddings.
83
+ - An **Azure OpenAI** resource deploys OpenAI models and serves chat
84
+ completions, embeddings, and the Responses API.
81
85
  - A **Microsoft Foundry** resource serves those plus every Foundry model
82
86
  family—Grok, DeepSeek, Llama, Mistral, Phi, Kimi and more—and serves **Claude
83
87
  models** on the native Anthropic Messages API, with chat completions through
@@ -67,5 +67,5 @@ list—so clients that can't set a model still work.
67
67
  | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
68
68
  | `/v1/chat/completions` | Chat completions for every provider |
69
69
  | `/v1/embeddings` | Embeddings for the providers marked in the [capability matrix](./providers.mdx#supported-providers) |
70
- | `/v1/responses` | OpenAI Responses API: OpenAI, and [Bedrock Mantle](./bedrock-mantle.mdx) OpenAI-compatible models that serve it |
70
+ | `/v1/responses` | OpenAI Responses API: OpenAI, and the OpenAI-compatible models that serve it on [Azure AI](./azure-ai.mdx) and [Bedrock Mantle](./bedrock-mantle.mdx) |
71
71
  | `/v1/messages` | Anthropic Messages API: Anthropic, and the Claude models of [Azure AI](./azure-ai.mdx), [Bedrock Mantle](./bedrock-mantle.mdx) and [Vertex AI](./vertex-ai.mdx) |
@@ -129,7 +129,12 @@ send your traffic and your spend to someone else's Google Cloud project.
129
129
  | `/v1/chat/completions` | ✅ Forwarded | ❌ |
130
130
  | `/v1/embeddings` | ✅ Translated | ❌ |
131
131
  | `/v1/responses` | ❌ | ❌ |
132
- | `/v1/messages` | ❌ | ✅ Native |
132
+ | `/v1/messages` | ❌ | ✅ Forwarded |
133
+
134
+ **Forwarded** means the gateway sends your request on in the shape you wrote it.
135
+ **Translated** means it converts between two API shapes—for embeddings, from the
136
+ OpenAI shape onto Vertex's own embedding APIs, which is invisible from your
137
+ side.
133
138
 
134
139
  Streaming (`stream: true`) works on chat completions and on Messages.
135
140
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuplo",
3
- "version": "7.7.9",
3
+ "version": "7.7.10",
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.7.9",
36
- "@zuplo/core": "7.7.9",
37
- "@zuplo/runtime": "7.7.9",
38
- "@zuplo/test": "7.7.9"
35
+ "@zuplo/cli": "7.7.10",
36
+ "@zuplo/core": "7.7.10",
37
+ "@zuplo/runtime": "7.7.10",
38
+ "@zuplo/test": "7.7.10"
39
39
  }
40
40
  }