zuplo 7.9.16 → 7.9.17
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.
|
@@ -90,6 +90,11 @@ use the app's API key and API URL instead.
|
|
|
90
90
|
|
|
91
91
|
`ANTHROPIC_AUTH_TOKEN` replaces your `claude.ai` login.
|
|
92
92
|
|
|
93
|
+
Add `"CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY": "1"` to `env` to add your
|
|
94
|
+
gateway's Claude models to the `/model` picker. See Claude Code's
|
|
95
|
+
[gateway model discovery](https://code.claude.com/docs/en/llm-gateway-protocol#model-discovery)
|
|
96
|
+
docs for details.
|
|
97
|
+
|
|
93
98
|
## Use your Claude subscription
|
|
94
99
|
|
|
95
100
|
This approach is shown with an app. On the User App, turning on passthrough
|
|
@@ -99,6 +104,13 @@ subscription logins.
|
|
|
99
104
|
Claude Code stays signed in to `claude.ai`. The gateway forwards that login to
|
|
100
105
|
Anthropic and reads the app's API key from a separate `zp-gateway-api-key`
|
|
101
106
|
header, so it still authenticates the app, runs its policies, and meters usage.
|
|
107
|
+
Model discovery (`CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY`) doesn't work with
|
|
108
|
+
a subscription login; list the models you need with the `ANTHROPIC_*_MODEL`
|
|
109
|
+
variables and Claude Code's
|
|
110
|
+
[`modelPicker`](https://code.claude.com/docs/en/settings-reference#modelpicker)
|
|
111
|
+
setting instead. **Put `modelPicker` in `~/.claude/settings.json` in your home
|
|
112
|
+
directory, not in a project's `.claude/settings.json`. Claude Code ignores it in
|
|
113
|
+
project settings.**
|
|
102
114
|
|
|
103
115
|
### Configure the API Key Authentication policy
|
|
104
116
|
|
|
@@ -23,11 +23,11 @@ agentic, document_qa, other) and the built-in classification prompt.
|
|
|
23
23
|
|
|
24
24
|
## Choose a classifier
|
|
25
25
|
|
|
26
|
-
| Classifier | Set | Use it when
|
|
27
|
-
| ----------------------------------------- | ----------------- |
|
|
28
|
-
| `classifier.app` | `appId`, `model` | You want the classifier to use one of your providers, through an AI Gateway app in this project. The call stays in-process. Set `adapter: "systemOne"` for Jev and other System One models. |
|
|
29
|
-
| `classifier.chatCompletions` (deprecated) | `apiKey`, `model` | You need to call an OpenAI-compatible endpoint directly with its own key, without a second AI Gateway app.
|
|
30
|
-
| `classifier.jev` (deprecated) | `apiKey` | You need to call Jev, from TypeSafe, directly with its own key. TypeSafe and OpenRouter both serve it.
|
|
26
|
+
| Classifier | Set | Use it when |
|
|
27
|
+
| ----------------------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| `classifier.app` | `appId`, `model` | You want the classifier to use one of your providers, through an AI Gateway app in this project. The call stays in-process. Set `adapter: "messages"` for Claude models, or `adapter: "systemOne"` for Jev and other System One models. |
|
|
29
|
+
| `classifier.chatCompletions` (deprecated) | `apiKey`, `model` | You need to call an OpenAI-compatible endpoint directly with its own key, without a second AI Gateway app. |
|
|
30
|
+
| `classifier.jev` (deprecated) | `apiKey` | You need to call Jev, from TypeSafe, directly with its own key. TypeSafe and OpenRouter both serve it. |
|
|
31
31
|
|
|
32
32
|
The three are mutually exclusive. Setting more than one is rejected with a
|
|
33
33
|
`ConfigurationError` rather than guessed at, because routing on a classifier you
|
|
@@ -50,8 +50,9 @@ budgets, and analytics, and new classifier features are added only to
|
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
- `appId` — the AI Gateway app that runs the classifier.
|
|
53
|
-
- `adapter` — optional: `chatCompletions` (default) or `systemOne`.
|
|
54
|
-
`/v1/chat/completions
|
|
53
|
+
- `adapter` — optional: `chatCompletions` (default), `messages`, or `systemOne`.
|
|
54
|
+
Selects `/v1/chat/completions`, `/v1/messages`, or `/v1/systemone` on that
|
|
55
|
+
app.
|
|
55
56
|
- `model` — the `providerName/model` that app routes the classifier request to.
|
|
56
57
|
- `apiKey` — optional. See
|
|
57
58
|
[Authenticating the classifier app](#authenticating-the-classifier-app).
|
|
@@ -61,6 +62,54 @@ policy runs on. The classifier request never leaves the gateway, so that
|
|
|
61
62
|
application needs no API key checked at all — put the Ensure Gateway Internal
|
|
62
63
|
Invocation Only policy on it in place of API Key Authentication.
|
|
63
64
|
|
|
65
|
+
For Claude models registered with your gateway, use the same app path with the
|
|
66
|
+
Anthropic Messages API:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
"classifier": {
|
|
70
|
+
"app": {
|
|
71
|
+
"appId": "$env(CLASSIFIER_APP_ID)",
|
|
72
|
+
"adapter": "messages",
|
|
73
|
+
"model": "anthropic/claude-haiku-4-5"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Replace `anthropic` with your configured provider name. Smart Router sends the
|
|
79
|
+
classifier prompt as `system`, the user prompt as the only message, and the
|
|
80
|
+
profile schema as `output_config.format`, so Claude's reply is constrained to
|
|
81
|
+
that JSON schema. Both the model and the provider must support
|
|
82
|
+
[structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs):
|
|
83
|
+
|
|
84
|
+
- **Model** — Claude Haiku 4.5 and the current Sonnet, Opus, and Fable models
|
|
85
|
+
support them.
|
|
86
|
+
- **Provider** — the provider's Messages endpoint must accept
|
|
87
|
+
`output_config.format`. Anthropic, Google Vertex AI, and Microsoft Foundry do.
|
|
88
|
+
Amazon Bedrock Mantle does not: its `/anthropic/v1/messages` endpoint rejects
|
|
89
|
+
`output_config.format` with a `400`, so every classification would fail. Use
|
|
90
|
+
another provider for the classifier app.
|
|
91
|
+
|
|
92
|
+
A model or provider without structured outputs rejects the request with a `400`,
|
|
93
|
+
which the policy logs and fails open on. The request allows up to 2,048 output
|
|
94
|
+
tokens, because thinking counts toward that limit on models that always think. A
|
|
95
|
+
reply cut off at that limit or at the model's context window, or a refusal, is
|
|
96
|
+
logged as a classifier error.
|
|
97
|
+
|
|
98
|
+
Use Claude Haiku 4.5 unless you've measured that a larger model classifies your
|
|
99
|
+
traffic better. Larger models such as Claude Opus 5.5 and Sonnet 5.5 always
|
|
100
|
+
think, which makes each classification slower and more expensive: at the 2,048
|
|
101
|
+
token limit, one classification on Claude Opus 5.5 can cost about $0.04. They
|
|
102
|
+
can also run past the default 8-second `classifierTimeoutMs`, which leaves the
|
|
103
|
+
request unclassified.
|
|
104
|
+
|
|
105
|
+
Anthropic compiles each new schema on first use and caches it for 24 hours. The
|
|
106
|
+
schema is built from your `intents`, so the first classification after you
|
|
107
|
+
change them, or after the cache expires, can be slower than usual. If it runs
|
|
108
|
+
past `classifierTimeoutMs`, that one request continues unclassified.
|
|
109
|
+
|
|
110
|
+
The `messages` adapter does not translate between providers: pick a model the
|
|
111
|
+
classifier app serves on `/v1/messages`.
|
|
112
|
+
|
|
64
113
|
For Jev registered with your gateway, use the same app path with System One:
|
|
65
114
|
|
|
66
115
|
```json
|
|
@@ -169,10 +218,11 @@ which can echo the request back, prompt included.
|
|
|
169
218
|
|
|
170
219
|
To migrate a direct section, register the provider with the gateway, create a
|
|
171
220
|
classifier app for it, and replace the section with `classifier.app`. Use
|
|
172
|
-
`adapter: "systemOne"` to replace `classifier.jev
|
|
173
|
-
|
|
174
|
-
omitted. `systemOne` uses the existing Jev
|
|
175
|
-
does not add support for arbitrary decision
|
|
221
|
+
`adapter: "systemOne"` to replace `classifier.jev`, and `adapter: "messages"`
|
|
222
|
+
for a Claude model. Existing `classifier.app` configurations continue to default
|
|
223
|
+
to Chat Completions when `adapter` is omitted. `systemOne` uses the existing Jev
|
|
224
|
+
request and response contract; it does not add support for arbitrary decision
|
|
225
|
+
APIs.
|
|
176
226
|
|
|
177
227
|
## Authenticating the classifier app
|
|
178
228
|
|
|
@@ -322,7 +372,8 @@ so it's never eligible for routing.
|
|
|
322
372
|
### Custom classifier prompt
|
|
323
373
|
|
|
324
374
|
`classifierPrompt` replaces the built-in classifier prompt for `classifier.app`
|
|
325
|
-
(its default
|
|
375
|
+
(its default `chatCompletions` adapter, or `messages`, which sends it as
|
|
376
|
+
`system`) and `classifier.chatCompletions`.
|
|
326
377
|
`classifier.app.adapter: "systemOne"` and `classifier.jev` take no prompt, so
|
|
327
378
|
combining `classifierPrompt` with either is a configuration error. It accepts
|
|
328
379
|
either a string or an array of lines:
|
|
@@ -77,8 +77,8 @@
|
|
|
77
77
|
"classifier": {
|
|
78
78
|
"app": {
|
|
79
79
|
"appId": "$env(CLASSIFIER_APP_ID)",
|
|
80
|
-
"adapter": "
|
|
81
|
-
"model": "
|
|
80
|
+
"adapter": "messages",
|
|
81
|
+
"model": "anthropic/claude-haiku-4-5"
|
|
82
82
|
}
|
|
83
83
|
},
|
|
84
84
|
"smartRoutingEnabled": true,
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"classifier": {
|
|
108
108
|
"type": "object",
|
|
109
109
|
"title": "Classifier",
|
|
110
|
-
"description": "Where the prompt is classified. Set
|
|
110
|
+
"description": "Where the prompt is classified. Set `app.adapter` to select the API that classifier app exposes.",
|
|
111
111
|
"additionalProperties": false,
|
|
112
112
|
"minProperties": 1,
|
|
113
113
|
"maxProperties": 1,
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"model": {
|
|
129
129
|
"type": "string",
|
|
130
130
|
"title": "Classifier Model",
|
|
131
|
-
"description": "The model (`providerName/model`) the classifier app uses to evaluate the user's request. `providerName` is the name of the provider as configured on the classifier app, not a fixed vendor id. E.g. `openai/gpt-4o-mini`, or `jev/jev-latest` when the Jev provider is named `jev`.",
|
|
131
|
+
"description": "The model (`providerName/model`) the classifier app uses to evaluate the user's request. `providerName` is the name of the provider as configured on the classifier app, not a fixed vendor id. E.g. `openai/gpt-4o-mini`, `anthropic/claude-haiku-4-5` with the `messages` adapter, or `jev/jev-latest` when the Jev provider is named `jev`.",
|
|
132
132
|
"pattern": "^[^/\\s]+/.+$",
|
|
133
133
|
"examples": ["openai/gpt-4o-mini"]
|
|
134
134
|
},
|
|
@@ -141,10 +141,10 @@
|
|
|
141
141
|
},
|
|
142
142
|
"adapter": {
|
|
143
143
|
"type": "string",
|
|
144
|
-
"enum": ["chatCompletions", "systemOne"],
|
|
144
|
+
"enum": ["chatCompletions", "messages", "systemOne"],
|
|
145
145
|
"default": "chatCompletions",
|
|
146
146
|
"title": "Classifier App Adapter",
|
|
147
|
-
"description": "API contract exposed by the classifier app. chatCompletions calls /v1/chat/completions; systemOne calls /v1/systemone for models such as Jev. The app owns provider credentials, pricing, metering and analytics."
|
|
147
|
+
"description": "API contract exposed by the classifier app. chatCompletions calls /v1/chat/completions; messages calls /v1/messages for Claude models that support structured outputs (`output_config.format`), such as `claude-haiku-4-5`, on a provider whose Messages endpoint accepts them (not Amazon Bedrock Mantle); systemOne calls /v1/systemone for models such as Jev. The app owns provider credentials, pricing, metering and analytics."
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
},
|
|
@@ -279,7 +279,7 @@
|
|
|
279
279
|
},
|
|
280
280
|
"classifierPrompt": {
|
|
281
281
|
"title": "Classifier Prompt",
|
|
282
|
-
"description": "Prompt for classifier.app (its default or
|
|
282
|
+
"description": "Prompt for classifier.app (its default chatCompletions adapter or the messages adapter, where it is sent as the system prompt) and classifier.chatCompletions. Omit to use the built-in prompt. classifier.app.adapter systemOne and classifier.jev take no prompt; combining them with classifierPrompt is a configuration error.",
|
|
283
283
|
"oneOf": [
|
|
284
284
|
{
|
|
285
285
|
"type": "string",
|
|
@@ -414,8 +414,8 @@
|
|
|
414
414
|
"classifier": {
|
|
415
415
|
"app": {
|
|
416
416
|
"appId": "$env(CLASSIFIER_APP_ID)",
|
|
417
|
-
"adapter": "
|
|
418
|
-
"model": "
|
|
417
|
+
"adapter": "messages",
|
|
418
|
+
"model": "anthropic/claude-haiku-4-5"
|
|
419
419
|
}
|
|
420
420
|
},
|
|
421
421
|
"smartRoutingEnabled": true,
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"pattern": "^[\\t\\x20-\\x7E\\x80-\\xFF]+$",
|
|
50
50
|
"examples": ["Not Found", "Unauthorized"],
|
|
51
51
|
"x-advanced": true,
|
|
52
|
-
"description": "The status text returned with `status`, such as `Not Found`. When it is omitted, the response uses the standard text for the status. Clients don't always receive it: HTTP/2 and HTTP/3 have no status text, and Managed Dedicated and self-hosted gateways always send the standard text."
|
|
52
|
+
"description": "The status text returned with `status`, such as `Not Found` for a `404`. When it is omitted, the response uses the standard text for the status. Clients don't always receive it: HTTP/2 and HTTP/3 have no status text, and Managed Dedicated and self-hosted gateways always send the standard text."
|
|
53
53
|
},
|
|
54
54
|
"detail": {
|
|
55
55
|
"type": "string",
|
|
@@ -57,7 +57,13 @@
|
|
|
57
57
|
"x-advanced": true,
|
|
58
58
|
"description": "The `detail` of the problem response returned to a rejected request. Every caller outside the gateway receives it, so don't describe how the route is called. When it is omitted, the response has no `detail`, and a `404` matches the response for a route that does not exist."
|
|
59
59
|
}
|
|
60
|
-
}
|
|
60
|
+
},
|
|
61
|
+
"examples": [
|
|
62
|
+
{
|
|
63
|
+
"status": 404,
|
|
64
|
+
"statusText": "Not Found"
|
|
65
|
+
}
|
|
66
|
+
]
|
|
61
67
|
}
|
|
62
68
|
},
|
|
63
69
|
"examples": [
|
|
@@ -65,7 +71,7 @@
|
|
|
65
71
|
"export": "InternalOnlyInboundPolicy",
|
|
66
72
|
"module": "$import(@zuplo/runtime)",
|
|
67
73
|
"options": {
|
|
68
|
-
"status":
|
|
74
|
+
"status": 404,
|
|
69
75
|
"statusText": "Not Found"
|
|
70
76
|
}
|
|
71
77
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zuplo",
|
|
3
|
-
"version": "7.9.
|
|
3
|
+
"version": "7.9.17",
|
|
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.9.
|
|
36
|
-
"@zuplo/core": "7.9.
|
|
37
|
-
"@zuplo/runtime": "7.9.
|
|
38
|
-
"@zuplo/test": "7.9.
|
|
35
|
+
"@zuplo/cli": "7.9.17",
|
|
36
|
+
"@zuplo/core": "7.9.17",
|
|
37
|
+
"@zuplo/runtime": "7.9.17",
|
|
38
|
+
"@zuplo/test": "7.9.17"
|
|
39
39
|
}
|
|
40
40
|
}
|