@fayegram-ai/decision-bridge 0.1.0 → 0.1.2
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/CHANGELOG.md +9 -0
- package/README.md +20 -14
- package/docs/README.md +5 -4
- package/docs/development/ARCHITECTURE.md +26 -3
- package/docs/development/DESIGN.md +11 -0
- package/docs/development/TESTING.md +23 -1
- package/docs/guides/BROWSER.md +3 -0
- package/docs/guides/CLOUDFLARE_DECISIONS.md +2 -2
- package/docs/guides/CUSTOM_PROVIDERS.md +4 -2
- package/docs/guides/GETTING_STARTED.md +1 -1
- package/docs/guides/JEV.md +174 -0
- package/docs/guides/OPENAI_DECISIONS.md +46 -4
- package/docs/guides/PLATFORMS.md +2 -2
- package/docs/guides/SWITCHING_MODELS.md +10 -4
- package/docs/guides/TROUBLESHOOTING.md +14 -0
- package/docs/reference/CLI.md +26 -4
- package/docs/reference/CONFIGURATION.md +12 -5
- package/docs/reference/ERRORS.md +5 -2
- package/docs/reference/PROVIDERS.md +20 -7
- package/docs/reference/SDK.md +22 -8
- package/docs/reference/SOURCES.md +4 -0
- package/examples/configs/openai-decisions.json +1 -1
- package/examples/decisions.native.json +1 -1
- package/package.json +1 -1
- package/schemas/decision.config.schema.json +15 -1
- package/src/capabilities.mjs +11 -8
- package/src/cli/config-validation.mjs +9 -1
- package/src/cli/config.mjs +17 -3
- package/src/cli/decisions.mjs +10 -0
- package/src/cli/initialize.mjs +8 -2
- package/src/cli/main.mjs +16 -4
- package/src/decisions.d.mts +5 -0
- package/src/index.d.mts +30 -8
- package/src/index.mjs +1 -1
- package/src/jev.d.mts +16 -0
- package/src/probabilities.mjs +6 -3
- package/src/providers/cloudflare-images.mjs +2 -58
- package/src/providers/decisions-preflight.mjs +18 -7
- package/src/providers/decisions-validation.mjs +3 -2
- package/src/providers/image-header.mjs +59 -0
- package/src/providers/jev-images.mjs +90 -0
- package/src/providers/jev-preflight.mjs +61 -0
- package/src/providers/jev-profile.mjs +37 -0
- package/src/providers/jev.mjs +32 -13
- package/src/providers/openai-decisions.mjs +17 -10
- package/src/providers/openai-profile.mjs +24 -0
- package/src/questions.mjs +2 -0
- package/src/validation.mjs +13 -6
- package/src/version.mjs +1 -1
package/docs/reference/CLI.md
CHANGED
|
@@ -20,8 +20,9 @@ it with `node bin/decision.mjs` from a source checkout. `decision` and
|
|
|
20
20
|
The CLI defaults to Jev. Flags override `DECISION_*` environment settings, which
|
|
21
21
|
override an explicit config file and then built-in defaults. It constructs an
|
|
22
22
|
explicit provider for the SDK; `createBridge` itself has no implicit selection.
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
Choose a supported native model from the
|
|
24
|
+
[model selector inventory](../../README.md#model-selectors).
|
|
25
|
+
Custom generative presets require an explicit model ID.
|
|
25
26
|
|
|
26
27
|
## Commands
|
|
27
28
|
|
|
@@ -32,6 +33,12 @@ for native request/response structures on `init`, `evaluate`, `validate`, or
|
|
|
32
33
|
or `-`. See [OpenAI Decisions](../guides/OPENAI_DECISIONS.md) and
|
|
33
34
|
[Cloudflare Clef](../guides/CLOUDFLARE_DECISIONS.md) for examples. Both providers report
|
|
34
35
|
model listing as unsupported.
|
|
36
|
+
OpenAI model selection defaults to `default`; use `--model luna` for the Luna
|
|
37
|
+
family or an explicit supported upstream ID. Both stable selectors currently
|
|
38
|
+
resolve to `gpt-6-luna` within Decision Bridge. OpenAI `validate` reports the
|
|
39
|
+
selected value in `model` and the outgoing ID in `resolvedModel`, without credentials.
|
|
40
|
+
Successful common and native calls include `meta.requestedModel` and
|
|
41
|
+
`meta.resolvedModel`; response models retain the service-reported identity.
|
|
35
42
|
Cloudflare CLI execution uses REST. The Workers AI binding is an SDK transport;
|
|
36
43
|
there is no CLI binding selector. Gateway settings route the selected REST request.
|
|
37
44
|
|
|
@@ -44,6 +51,12 @@ there is no CLI binding selector. Gateway settings route the selected REST reque
|
|
|
44
51
|
| `models` | List the configured provider's models; may be paginated |
|
|
45
52
|
| `batch FILE\|-` | Stream JSONL inputs and ordered JSONL result/error records |
|
|
46
53
|
|
|
54
|
+
Jev supports both common and `--native` requests for evaluation, validation,
|
|
55
|
+
initialization, and ordered batches. Experimental images/boxes require
|
|
56
|
+
`--jev-images`, and longer context requires `--jev-long-context` (or matching
|
|
57
|
+
JSON configuration). Use `--no-jev-images` and `--no-jev-long-context` to
|
|
58
|
+
override enabled settings. See the [Jev guide](../guides/JEV.md).
|
|
59
|
+
|
|
47
60
|
No input filename on `evaluate`, `validate`, or `schema` selects the bundled sample,
|
|
48
61
|
not stdin. Use `-` explicitly for stdin. Empty invocation prints help. Configuration
|
|
49
62
|
and credential errors do not cause fallback to another backend.
|
|
@@ -71,6 +84,10 @@ explicit service implementation error until you connect it. An existing
|
|
|
71
84
|
`--provider-module` is referenced without execution or overwrite. All profiles
|
|
72
85
|
include a copied JSON Schema for editor validation. See [custom providers](../guides/CUSTOM_PROVIDERS.md).
|
|
73
86
|
|
|
87
|
+
OpenAI initialization writes the selected model into configuration, using `default`
|
|
88
|
+
when omitted. Its generated common and native requests omit `model`, allowing
|
|
89
|
+
later configuration changes to control selection.
|
|
90
|
+
|
|
74
91
|
## Flags
|
|
75
92
|
|
|
76
93
|
| Flag | Meaning |
|
|
@@ -80,7 +97,9 @@ include a copied JSON Schema for editor validation. See [custom providers](../gu
|
|
|
80
97
|
| `--provider NAME` | `jev`, `openai-decisions`, `cloudflare-decisions`, `custom-decision`, `custom-generative` |
|
|
81
98
|
| `--preset NAME` | custom-generative preset: `openai-compatible`, `openrouter`, or `anthropic`; exclusive with module |
|
|
82
99
|
| `--native` | Explicit native Decisions input/output; init/evaluate/validate/batch only |
|
|
83
|
-
| `--
|
|
100
|
+
| `--jev-images`, `--no-jev-images` | Enable/disable experimental Jev images and boxes |
|
|
101
|
+
| `--jev-long-context`, `--no-jev-long-context` | Enable/disable experimental Jev 262k context |
|
|
102
|
+
| `--model SELECTOR` | Model selector or supported upstream ID; overrides input JSON model and configured default |
|
|
84
103
|
| `--base-url URL` | Custom endpoint; requires explicit credential selection |
|
|
85
104
|
| `--api-key-env NAME` | Environment variable name, never the key itself |
|
|
86
105
|
| `--account-id ID` | Cloudflare REST account ID |
|
|
@@ -161,7 +180,10 @@ delivery needs separate validation.
|
|
|
161
180
|
|
|
162
181
|
## I/O and resource limits
|
|
163
182
|
|
|
164
|
-
Single JSON input
|
|
183
|
+
Single JSON input normally uses **8 MiB**. JSONL normally uses **8 MiB per line,
|
|
184
|
+
64 MiB total**. Jev permits **32 MiB** for a single JSON input or JSONL record,
|
|
185
|
+
then checks serialized requests against its configured ceiling (32 MiB by default).
|
|
186
|
+
Config:
|
|
165
187
|
**1 MiB**. Environment file: **64 KiB**. Provider request and response default to
|
|
166
188
|
8 MiB each, configurable to at most 64 MiB. Cloudflare reads single JSON inputs
|
|
167
189
|
and individual JSONL records within 64 MiB, then applies model-specific serialized
|
|
@@ -40,16 +40,17 @@ profile. Gateway configuration is optional for either SDK transport and for CLI
|
|
|
40
40
|
| `schemaVersion` | 1; optional, but generated profiles include it |
|
|
41
41
|
| `provider` | `jev` default, `openai-decisions`, `cloudflare-decisions`, `custom-decision`, `custom-generative` |
|
|
42
42
|
| `preset` | `custom-generative` only: `openai-compatible`, `openrouter`, `anthropic`; mutually exclusive with module |
|
|
43
|
-
| `model` | Nonempty model ID; built-in native providers have defaults; required for generative evaluation unless the request supplies it |
|
|
43
|
+
| `model` | Nonempty model selector or supported upstream ID; built-in native providers have defaults; required for generative evaluation unless the request supplies it |
|
|
44
44
|
| `baseURL` | HTTPS API base; loopback may use HTTP |
|
|
45
45
|
| `apiKeyEnv` | Credential environment variable name; inline `apiKey` is rejected |
|
|
46
46
|
| `noAuth` | Boolean; true only for loopback OpenAI-compatible endpoints |
|
|
47
47
|
| `timeoutMs` | Positive integer up to 2,147,483,647 milliseconds |
|
|
48
48
|
| `maxRetries` | Integer 0–10; OpenAI/Cloudflare default 0; Jev and generative presets default 2 |
|
|
49
|
-
| `maxRequestBytes`, `maxResponseBytes` | Positive integer through 64 MiB; default 8 MiB. Cloudflare requests: Clef/Clef-flash default/cap 13 MiB; Omni default 36 MiB, configurable through 64 MiB |
|
|
49
|
+
| `maxRequestBytes`, `maxResponseBytes` | Positive integer through 64 MiB; default 8 MiB. Jev requests default/cap 32 MiB. Cloudflare requests: Clef/Clef-flash default/cap 13 MiB; Omni default 36 MiB, configurable through 64 MiB |
|
|
50
50
|
| `accountId` | Cloudflare REST account ID; otherwise `CLOUDFLARE_ACCOUNT_ID` |
|
|
51
51
|
| `rejectIfBusy` | Cloudflare boolean; default false, incompatible with retries. Effective true fails REST preflight with `UNSUPPORTED_CAPABILITY`; see the [binding support and REST limitation](../guides/CLOUDFLARE_DECISIONS.md#capacity-rejection-and-ai-gateway) |
|
|
52
52
|
| `gateway` | Cloudflare Gateway object; see the [Cloudflare guide](../guides/CLOUDFLARE_DECISIONS.md) |
|
|
53
|
+
| `dangerZone` | Jev-only `{ images?: boolean, longContext?: boolean }`; both default false; see the [Jev guide](../guides/JEV.md) |
|
|
53
54
|
| `maxTokens` | Generative output budget, 1–1,048,576; default 4096 |
|
|
54
55
|
| `responseFormat` | Compatible generative presets: `json_schema`, `json_object`, `text` |
|
|
55
56
|
| `tokenParameter` | Compatible generative presets: `max_completion_tokens` or `max_tokens` |
|
|
@@ -58,6 +59,10 @@ profile. Gateway configuration is optional for either SDK transport and for CLI
|
|
|
58
59
|
| `options` | Custom factory's JSON options, e.g. its own baseURL |
|
|
59
60
|
|
|
60
61
|
Upper numeric limits validate client configuration, not vendor model capabilities.
|
|
62
|
+
Jev requests default to and are capped at 32 MiB; a smaller `maxRequestBytes`
|
|
63
|
+
lowers that ceiling. `DECISION_JEV_IMAGES` and `DECISION_JEV_LONG_CONTEXT`
|
|
64
|
+
accept `true` or `false`; their CLI overrides are `--jev-images`,
|
|
65
|
+
`--no-jev-images`, `--jev-long-context`, and `--no-jev-long-context`.
|
|
61
66
|
Cloudflare selects its byte budget from the effective request model, including
|
|
62
67
|
per-call overrides. An explicit smaller `maxRequestBytes` remains the ceiling;
|
|
63
68
|
selecting Omni cannot enlarge it. Omni's default is a bridge policy, not a
|
|
@@ -88,8 +93,8 @@ existing process environment > --env-file values
|
|
|
88
93
|
The loader does not mutate the process environment. The SDK independently reads
|
|
89
94
|
its documented ambient credential variables at provider construction.
|
|
90
95
|
|
|
91
|
-
The [
|
|
92
|
-
selectors
|
|
96
|
+
The [model selector list](../../README.md#model-selectors) records native
|
|
97
|
+
selectors and defaults. Configuration precedence does not expand a
|
|
93
98
|
provider's accepted models or grant account access.
|
|
94
99
|
|
|
95
100
|
For evaluation model selection:
|
|
@@ -99,7 +104,9 @@ For evaluation model selection:
|
|
|
99
104
|
```
|
|
100
105
|
|
|
101
106
|
Jev defaults to `jev-latest`, with `JEV_MODEL` as a legacy/direct convenience.
|
|
102
|
-
Native OpenAI Decisions defaults to `
|
|
107
|
+
Native OpenAI Decisions defaults to `default` and accepts `luna` or explicit
|
|
108
|
+
`gpt-6-luna`; Decision Bridge currently resolves each to upstream `gpt-6-luna`.
|
|
109
|
+
OpenAI accepts transport settings
|
|
103
110
|
and rejects generative output settings. Its default retry count is zero. Cloudflare Clef defaults to `clef`, uses
|
|
104
111
|
account/token credentials, rejects generation settings, and also defaults to zero retries.
|
|
105
112
|
A `DECISION_MODEL` or file model takes precedence over that convenience.
|
package/docs/reference/ERRORS.md
CHANGED
|
@@ -77,6 +77,9 @@ validation failures can still return HTTP 422/5012 and retain safe diagnostics.
|
|
|
77
77
|
Keep the default disabled setting for ordinary REST
|
|
78
78
|
inference; see the [Cloudflare guide](../guides/CLOUDFLARE_DECISIONS.md#capacity-rejection-and-ai-gateway).
|
|
79
79
|
|
|
80
|
-
Keep a request ID and available Gateway log ID, bridge version, configured provider,
|
|
81
|
-
and template revision for incident analysis.
|
|
80
|
+
Keep a request ID and available Gateway log ID, bridge version, configured provider,
|
|
81
|
+
model selection, reported model, and template revision for incident analysis.
|
|
82
|
+
For successful OpenAI calls, retain
|
|
83
|
+
the requested selection, resolved outgoing ID, and reported model separately;
|
|
84
|
+
see [model identities](../guides/OPENAI_DECISIONS.md#model-selection). Log input or results only under your
|
|
82
85
|
own data-handling policy. No logging or telemetry is enabled by default.
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## Native routes
|
|
4
4
|
|
|
5
|
-
| Provider type | Wire API | Credential | Default
|
|
5
|
+
| Provider type | Wire API | Credential | Default selector | Output strategy |
|
|
6
6
|
| --- | --- | --- | --- | --- |
|
|
7
7
|
| `jev` | `POST /v1/systemone` at api.typesafe.ai | `TYPESAFE_API_KEY` | `jev-latest` | Native typed decisions |
|
|
8
|
-
| `openai-decisions` | `POST /v1/decisions` at api.openai.com | `OPENAI_API_KEY` | `
|
|
8
|
+
| `openai-decisions` | `POST /v1/decisions` at api.openai.com | `OPENAI_API_KEY` | `default` | Native Decisions with common and dedicated access |
|
|
9
9
|
| `cloudflare-decisions` | Workers AI REST model route or `AI.run` binding | `CLOUDFLARE_API_TOKEN` and account ID for REST | `clef` | Native Clef decisions with common and dedicated access |
|
|
10
10
|
|
|
11
11
|
`createProvider()` and the root SDK export these three native providers only.
|
|
@@ -15,9 +15,12 @@ maintained, but are not native decision models. See [custom providers](../guides
|
|
|
15
15
|
|
|
16
16
|
## Supported model selection
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
built-in
|
|
20
|
-
its account catalog. OpenAI Decisions
|
|
18
|
+
The [model selector inventory](../../README.md#model-selectors) summarizes the
|
|
19
|
+
built-in providers. Jev accepts `jev-latest` and explicit Jev IDs from
|
|
20
|
+
its account catalog. OpenAI Decisions accepts `default`, `luna`, and explicit
|
|
21
|
+
`gpt-6-luna`. Decision Bridge resolves these to the upstream `gpt-6-luna` model
|
|
22
|
+
in this release. The selected value remains in `meta.requestedModel`, the outgoing
|
|
23
|
+
ID in `meta.resolvedModel`, and the service-reported identity in the response's `model`.
|
|
21
24
|
Cloudflare supports `clef`, `clef-flash`, and `clef-omni`, including each full
|
|
22
25
|
`@cf/cloudflare/<model>` alias. Unsupported OpenAI/Cloudflare selectors fail
|
|
23
26
|
local preflight with `UNSUPPORTED_CAPABILITY`.
|
|
@@ -41,7 +44,7 @@ contract. The following additional forms are checked before dispatch:
|
|
|
41
44
|
|
|
42
45
|
| Provider | Ordered text / embedded images | Typed string/boolean choices | Labelled score levels | Safety identifier | Model listing |
|
|
43
46
|
| --- | --- | --- | --- | --- | --- |
|
|
44
|
-
| Jev |
|
|
47
|
+
| Jev | Ordered text; images with explicit opt-in | Unsupported | Unsupported | Unsupported | Supported |
|
|
45
48
|
| OpenAI Decisions | Supported; up to 128 images, preserving order | Supported | Supported | Supported | Unsupported |
|
|
46
49
|
| Cloudflare Clef / Clef-flash | Supported; up to four images before text | Supported | Supported; 2–10 levels | Unsupported | Unsupported |
|
|
47
50
|
| Cloudflare Clef-omni | Supported; up to four images before audio/video/text | Supported | Supported; 2–10 levels | Unsupported | Unsupported |
|
|
@@ -59,7 +62,11 @@ capability fields `audio`, `videos`, `maxAudioClips`, and `maxVideos` normalize
|
|
|
59
62
|
false/zero when omitted, preserving legacy custom profiles. Cloudflare's older
|
|
60
63
|
models and all other shipped providers reject audio/video before inference.
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
Jev additionally supports `box` questions on `jev-preview` and pinned 1.14+ models
|
|
66
|
+
when `dangerZone.images` is enabled. Other built-ins reject box questions.
|
|
67
|
+
See the [Jev guide](../guides/JEV.md) for asset placement and experimental opt-ins.
|
|
68
|
+
|
|
69
|
+
Dedicated `createDecision()` access is available on Jev, OpenAI Decisions and
|
|
63
70
|
Cloudflare providers. It uses each service's native request/response shape and
|
|
64
71
|
bypasses bridge preparation and observation hooks.
|
|
65
72
|
|
|
@@ -71,6 +78,12 @@ intermediate language model, skill, portal prompt, or chat conversion is used.
|
|
|
71
78
|
`GET /v1/models` lists native models. A listed catalog can differ from valid aliases;
|
|
72
79
|
`jev-latest` is the documented stable alias.
|
|
73
80
|
|
|
81
|
+
`jev-preview` selects the preview release. Images and longer context require
|
|
82
|
+
explicit `dangerZone` settings. Common image input preserves ordering; native
|
|
83
|
+
`createDecision()` additionally accepts JSON assets referenced in state,
|
|
84
|
+
instructions, and criteria. Box outputs preserve normalized edges and confidence.
|
|
85
|
+
See the [Jev guide](../guides/JEV.md) for limits and absence handling.
|
|
86
|
+
|
|
74
87
|
Pin a version after evaluating it for your workload rather than assuming a moving
|
|
75
88
|
alias yields reproducible quality. Store the returned `model` along with the
|
|
76
89
|
requested model and your question-template revision.
|
package/docs/reference/SDK.md
CHANGED
|
@@ -48,7 +48,7 @@ guides. These are SDK options, not per-evaluation bridge options.
|
|
|
48
48
|
| --- | --- |
|
|
49
49
|
| `timeoutMs` | 30,000 ms; positive integer through 2,147,483,647; one transport budget for dispatch, retries, backoff, and response reading |
|
|
50
50
|
| `maxRetries` | Additional eligible attempts, 0–10; Jev and generative presets default to 2, native OpenAI and Cloudflare to 0 |
|
|
51
|
-
| `maxRequestBytes` | Complete serialized request ceiling; normally 8 MiB, configurable from 1 byte through 64 MiB; Cloudflare applies its [model-specific budgets](../guides/CLOUDFLARE_DECISIONS.md#embedded-images-and-limits) |
|
|
51
|
+
| `maxRequestBytes` | Complete serialized request ceiling; normally 8 MiB, configurable from 1 byte through 64 MiB; Jev defaults to and caps at 32 MiB; Cloudflare applies its [model-specific budgets](../guides/CLOUDFLARE_DECISIONS.md#embedded-images-and-limits) |
|
|
52
52
|
| `maxResponseBytes` | 8 MiB; configurable from 1 byte through 64 MiB; applies while reading the response body |
|
|
53
53
|
| `fetchImpl` | Optional `(url: string, init?: RequestInit) => Promise<Response>`; defaults to the runtime's `fetch` |
|
|
54
54
|
|
|
@@ -72,7 +72,10 @@ they interact; [errors](ERRORS.md#retry-policy) lists retryable statuses.
|
|
|
72
72
|
`model` and `safetyIdentifier`. New forms require declared provider capabilities;
|
|
73
73
|
see [OpenAI Decisions](../guides/OPENAI_DECISIONS.md) and
|
|
74
74
|
[Cloudflare Clef](../guides/CLOUDFLARE_DECISIONS.md). The request model
|
|
75
|
-
wins over the provider's
|
|
75
|
+
wins over the provider's configured default, which an omitted request model inherits.
|
|
76
|
+
OpenAI Decisions uses the `default` selector when its provider configuration omits
|
|
77
|
+
`model`; `luna` and explicit `gpt-6-luna` are also supported. Its selectors
|
|
78
|
+
resolve to upstream IDs within the provider. No default is assumed for a generative provider.
|
|
76
79
|
`options` accepts `signal: AbortSignal` and `timeoutMs`, overriding the bridge's
|
|
77
80
|
60-second evaluation budget for this call. It does not mutate the provider's
|
|
78
81
|
independent transport timeout, including Workers AI binding calls.
|
|
@@ -92,10 +95,12 @@ when client retries are configured.
|
|
|
92
95
|
### `bridge.getCapabilities(model?)` and `bridge.validate(request)`
|
|
93
96
|
|
|
94
97
|
Capability lookup is synchronous and reports implemented features and limits.
|
|
95
|
-
`validate`
|
|
98
|
+
`validate` applies the default model selection, snapshots/freezes the request, and checks capabilities
|
|
96
99
|
and preparation-extension compatibility without inference. Unsupported features
|
|
97
100
|
fail with `UNSUPPORTED_CAPABILITY` before dispatch. Native Decisions has no model
|
|
98
101
|
listing operation; capability lookup is not account model discovery.
|
|
102
|
+
OpenAI validated requests retain the selected value, while preflight checks the
|
|
103
|
+
resolved outgoing request, including its model ID, against the byte limit.
|
|
99
104
|
|
|
100
105
|
Providers can implement optional `validateRequest(resolvedRequest)` for additional
|
|
101
106
|
static checks. This synchronous hook receives a frozen request, must return no
|
|
@@ -141,6 +146,10 @@ const q3 = score('How far did processing progress?', [
|
|
|
141
146
|
]);
|
|
142
147
|
```
|
|
143
148
|
|
|
149
|
+
`box(instructions)` returns a box question with no criteria; supported providers
|
|
150
|
+
must declare `boxes: true`. Jev supports it with experimental image opt-in and
|
|
151
|
+
a supported model; see [Jev images and boxes](../guides/JEV.md).
|
|
152
|
+
|
|
144
153
|
Builders return immutable question objects. `choice(instructions, criteria)` accepts
|
|
145
154
|
a label map containing 1 to 255 options. `noul(instructions, criteria?)` accepts an
|
|
146
155
|
optional criteria object,
|
|
@@ -196,7 +205,8 @@ rubric exactly. Generative providers calculate score and legend locally. Native
|
|
|
196
205
|
values are validated and preserved, not normalized or replaced.
|
|
197
206
|
|
|
198
207
|
**Usage:** `input_tokens` and `output_tokens` are nonnegative integers or null.
|
|
199
|
-
Jev
|
|
208
|
+
Jev accepts explicitly null counts as unknown; OpenAI Decisions and Cloudflare
|
|
209
|
+
require reported integer counts. OpenAI and
|
|
200
210
|
Anthropic cache creation/read counts are preserved separately when provided;
|
|
201
211
|
Cloudflare does not fabricate cache or total-token fields. No total price, inferred cache saving, or
|
|
202
212
|
estimated token count is fabricated.
|
|
@@ -207,7 +217,8 @@ estimated token count is fabricated.
|
|
|
207
217
|
| --- | --- |
|
|
208
218
|
| `provider` | Bridge backend name; native Jev reports `typesafe` |
|
|
209
219
|
| `providerKind` | `decision` or `generative`; authoritative provider classification, never response-selected |
|
|
210
|
-
| `requestedModel` |
|
|
220
|
+
| `requestedModel` | Selected model after applying the provider default; OpenAI retains `default`, `luna`, or the explicit ID |
|
|
221
|
+
| `resolvedModel` | Upstream model ID sent by OpenAI Decisions; optional for other providers |
|
|
211
222
|
| `probabilitySource` | `native`, `elicited`, or custom/`unspecified` provenance |
|
|
212
223
|
| `confidenceSource` | `native` or `unavailable` for built-in providers |
|
|
213
224
|
| `modelSource` | `reported` or `requested` fallback for built-in providers |
|
|
@@ -225,7 +236,8 @@ unavailable metadata exists. Result objects and nested data returned by the brid
|
|
|
225
236
|
are immutable. Standalone validation functions return snapshots, not frozen values.
|
|
226
237
|
|
|
227
238
|
`schemaVersion` remains 1 for legacy requests and 2 for ordered images/text or
|
|
228
|
-
richer question forms. Audio/video requests use 3
|
|
239
|
+
richer question forms. Audio/video requests use 3; requests with boxes use 4.
|
|
240
|
+
This result version is separate
|
|
229
241
|
from the CLI configuration schema, which remains 1.
|
|
230
242
|
|
|
231
243
|
## Extensions
|
|
@@ -298,6 +310,8 @@ automatic model fallback. The CLI has a separate streaming JSONL implementation.
|
|
|
298
310
|
- `createProvider(config)`: factory requiring an explicit `type` equal to `jev`,
|
|
299
311
|
`openai-decisions`, or `cloudflare-decisions`.
|
|
300
312
|
- `JevProvider`, `OpenAIDecisionsProvider`, `CloudflareDecisionsProvider`: native provider classes.
|
|
313
|
+
- `box(instructions)` and `JevProvider.createDecision(request, options?)`:
|
|
314
|
+
bounding boxes and native JSON assets; see the [Jev guide](../guides/JEV.md).
|
|
301
315
|
- `scoreLevels(instructions, levels)` and the array overload of `choice`:
|
|
302
316
|
labelled rubrics and typed string/boolean options, with provider capability checks.
|
|
303
317
|
- `OpenAIDecisionsProvider.createDecision(request, options?)`: dedicated native
|
|
@@ -308,9 +322,9 @@ automatic model fallback. The CLI has a separate streaming JSONL implementation.
|
|
|
308
322
|
an own `input.model` property or an explicitly supplied default; inherited
|
|
309
323
|
models are ignored, and it never selects a Jev model.
|
|
310
324
|
Question builders and schema generation validate without selecting an inference model.
|
|
311
|
-
- `validateResult(input, questions, { native = false, probabilityDecimalPlaces })`: snapshot supplied
|
|
325
|
+
- `validateResult(input, questions, { native = false, nullableUsage = false, probabilityDecimalPlaces })`: snapshot supplied
|
|
312
326
|
questions and validate the canonical result; native mode requires confidence
|
|
313
|
-
and reported counts.
|
|
327
|
+
and reported counts unless the provider explicitly enables nullable usage.
|
|
314
328
|
- `decideChoice(answer, { minProbability, minMargin = 0, abstainOptions = [], probabilityDecimalPlaces })`:
|
|
315
329
|
returns a policy result, never executes an action. `minProbability` is required.
|
|
316
330
|
- `DecisionError`, `publicError(error)`: public structured failure handling.
|
|
@@ -11,6 +11,10 @@ reference does not imply that authenticated inference was run against its servic
|
|
|
11
11
|
| TypeSafe request/response API | https://docs.typesafe.ai/api |
|
|
12
12
|
| TypeSafe JavaScript examples | https://docs.typesafe.ai/sdk/javascript |
|
|
13
13
|
| Native model listing and aliases | https://docs.typesafe.ai/models |
|
|
14
|
+
| Jev image assets and experimental opt-in | https://docs.typesafe.ai/images |
|
|
15
|
+
| Jev bounding boxes | https://docs.typesafe.ai/primitives/box |
|
|
16
|
+
| Jev 1.14 release | https://typesafe.ai/blog/jev-1-14 |
|
|
17
|
+
| Jev nullable usage compatibility | https://docs.typesafe.ai/sdk/javascript/changelog |
|
|
14
18
|
| Choice decisions | https://docs.typesafe.ai/primitives/choice |
|
|
15
19
|
| Noul probabilities | https://docs.typesafe.ai/primitives/noul |
|
|
16
20
|
| Score rubric and weighted expectation | https://docs.typesafe.ai/primitives/score |
|
package/package.json
CHANGED
|
@@ -29,13 +29,27 @@
|
|
|
29
29
|
"$ref": "#/$defs/retries"
|
|
30
30
|
},
|
|
31
31
|
"maxRequestBytes": {
|
|
32
|
-
"
|
|
32
|
+
"type": "integer",
|
|
33
|
+
"minimum": 1,
|
|
34
|
+
"maximum": 33554432
|
|
33
35
|
},
|
|
34
36
|
"maxResponseBytes": {
|
|
35
37
|
"$ref": "#/$defs/bytes"
|
|
36
38
|
},
|
|
37
39
|
"provider": {
|
|
38
40
|
"const": "jev"
|
|
41
|
+
},
|
|
42
|
+
"dangerZone": {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"additionalProperties": false,
|
|
45
|
+
"properties": {
|
|
46
|
+
"images": {
|
|
47
|
+
"type": "boolean"
|
|
48
|
+
},
|
|
49
|
+
"longContext": {
|
|
50
|
+
"type": "boolean"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
39
53
|
}
|
|
40
54
|
}
|
|
41
55
|
},
|
package/src/capabilities.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { DecisionError, requireCondition as check } from './errors.mjs';
|
|
2
2
|
import { cloneJson, freezeJson, isRecord } from './validation.mjs';
|
|
3
3
|
import { CLOUDFLARE_MODELS } from './providers/cloudflare-profile.mjs';
|
|
4
|
+
import { openaiModelProfile } from './providers/openai-profile.mjs';
|
|
5
|
+
import { jevFeatures } from './providers/jev-profile.mjs';
|
|
4
6
|
|
|
5
7
|
/** Conservative defaults preserve the pre-existing custom-provider contract. */
|
|
6
8
|
export const LEGACY_CAPABILITIES = freezeJson({
|
|
@@ -8,11 +10,7 @@ export const LEGACY_CAPABILITIES = freezeJson({
|
|
|
8
10
|
labeledScores: false, safetyIdentifier: false,
|
|
9
11
|
maxImages: 0, maxChoiceOptions: 255, maxScoreLevels: 10,
|
|
10
12
|
audio: false, videos: false, maxAudioClips: 0, maxVideos: 0,
|
|
11
|
-
|
|
12
|
-
const OPENAI_CAPABILITIES = freezeJson({
|
|
13
|
-
supported: true, orderedInput: true, images: true, typedChoices: true,
|
|
14
|
-
labeledScores: true, safetyIdentifier: true,
|
|
15
|
-
maxImages: 128, maxChoiceOptions: 255, maxScoreLevels: null,
|
|
13
|
+
boxes: false,
|
|
16
14
|
});
|
|
17
15
|
|
|
18
16
|
const CLOUDFLARE_CAPABILITIES = freezeJson({
|
|
@@ -20,7 +18,9 @@ const CLOUDFLARE_CAPABILITIES = freezeJson({
|
|
|
20
18
|
labeledScores: true, safetyIdentifier: false, maxImages: 4, maxChoiceOptions: 255, maxScoreLevels: 10,
|
|
21
19
|
});
|
|
22
20
|
|
|
23
|
-
export function builtInCapabilities(type, model) {
|
|
21
|
+
export function builtInCapabilities(type, model, options = {}) {
|
|
22
|
+
if (type === 'jev') return freezeJson({ ...LEGACY_CAPABILITIES,
|
|
23
|
+
...jevFeatures(model, Object.hasOwn(options, 'dangerZone') ? options.dangerZone : undefined) });
|
|
24
24
|
if (type === 'cloudflare-decisions') {
|
|
25
25
|
const key = typeof model === 'string' ? model.trim().replace(/^@cf\/cloudflare\//, '') : '';
|
|
26
26
|
const profile = Object.hasOwn(CLOUDFLARE_MODELS, key) ? CLOUDFLARE_MODELS[key] : undefined;
|
|
@@ -29,7 +29,8 @@ export function builtInCapabilities(type, model) {
|
|
|
29
29
|
maxAudioClips: profile?.omni ? 4 : 0, maxVideos: profile?.omni ? 2 : 0 });
|
|
30
30
|
}
|
|
31
31
|
if (type !== 'openai-decisions') return LEGACY_CAPABILITIES;
|
|
32
|
-
|
|
32
|
+
const profile = openaiModelProfile(model);
|
|
33
|
+
return freezeJson({ ...LEGACY_CAPABILITIES, ...profile, supported: !!profile });
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export function providerCapabilities(provider, model) {
|
|
@@ -48,7 +49,7 @@ export function providerCapabilities(provider, model) {
|
|
|
48
49
|
check(isRecord(profile) && Object.keys(profile).every(key => Object.hasOwn(LEGACY_CAPABILITIES, key)),
|
|
49
50
|
'Invalid capability profile.', 'CONFIGURATION_ERROR');
|
|
50
51
|
const result = { ...LEGACY_CAPABILITIES, ...profile };
|
|
51
|
-
for (const key of ['supported', 'orderedInput', 'images', 'typedChoices', 'labeledScores', 'safetyIdentifier', 'audio', 'videos']) {
|
|
52
|
+
for (const key of ['supported', 'orderedInput', 'images', 'typedChoices', 'labeledScores', 'safetyIdentifier', 'audio', 'videos', 'boxes']) {
|
|
52
53
|
check(typeof result[key] === 'boolean', 'Capability flags must be boolean.', 'CONFIGURATION_ERROR');
|
|
53
54
|
}
|
|
54
55
|
for (const key of ['maxImages', 'maxChoiceOptions', 'maxScoreLevels', 'maxAudioClips', 'maxVideos']) {
|
|
@@ -78,6 +79,7 @@ export function assertCapabilities(request, capabilities) {
|
|
|
78
79
|
}
|
|
79
80
|
if (Object.hasOwn(request, 'safetyIdentifier')) requireFeature(capabilities.safetyIdentifier, 'safetyIdentifier');
|
|
80
81
|
for (const q of Object.values(request.questions)) {
|
|
82
|
+
if (q.type === 'box') requireFeature(capabilities.boxes, 'box questions');
|
|
81
83
|
if (q.options) requireFeature(capabilities.typedChoices, 'typed choices');
|
|
82
84
|
if (q.levels) requireFeature(capabilities.labeledScores, 'labelled scores');
|
|
83
85
|
const choices = q.options?.length ?? (q.type === 'choice' ? Object.keys(q.criteria).length : 0);
|
|
@@ -89,6 +91,7 @@ export function assertCapabilities(request, capabilities) {
|
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
export function commonSchemaVersion(request) {
|
|
94
|
+
if (Object.values(request.questions).some(q => q.type === 'box')) return 4;
|
|
92
95
|
if (request.input?.some(part => ['audio', 'video'].includes(part.type))) return 3;
|
|
93
96
|
return Object.hasOwn(request, 'input') || Object.hasOwn(request, 'safetyIdentifier') ||
|
|
94
97
|
Object.values(request.questions).some(q => q.options || q.levels) ? 2 : 1;
|
|
@@ -2,13 +2,14 @@ import { requireCondition as check } from '../errors.mjs';
|
|
|
2
2
|
import { cloneJson, isRecord } from '../validation.mjs';
|
|
3
3
|
import { cloudflareGateway, validateCloudflareLimits } from '../providers/cloudflare-profile.mjs';
|
|
4
4
|
import { generationOptions } from '../custom/generative/shared.mjs';
|
|
5
|
+
import { jevDangerZone, jevRequestLimit } from '../providers/jev-profile.mjs';
|
|
5
6
|
|
|
6
7
|
export const PROVIDERS = ['jev', 'openai-decisions', 'cloudflare-decisions', 'custom-decision', 'custom-generative'];
|
|
7
8
|
export const PRESETS = ['openai-compatible', 'openrouter', 'anthropic'];
|
|
8
9
|
export const TRANSPORT = ['baseURL', 'maxRetries', 'maxRequestBytes', 'maxResponseBytes'];
|
|
9
10
|
export const GENERATION = ['maxTokens', 'responseFormat', 'tokenParameter', 'parameters', 'noAuth'];
|
|
10
11
|
export const PROVIDER_FIELDS = [...TRANSPORT, ...GENERATION, 'apiKeyEnv', 'model', 'preset', 'module',
|
|
11
|
-
'options', 'accountId', 'rejectIfBusy', 'gateway'];
|
|
12
|
+
'options', 'accountId', 'rejectIfBusy', 'gateway', 'dangerZone'];
|
|
12
13
|
const FIELDS = new Set(['$schema', 'schemaVersion', 'provider', 'timeoutMs', ...PROVIDER_FIELDS]);
|
|
13
14
|
export const NUMERIC = new Set(['timeoutMs', 'maxRetries', 'maxRequestBytes', 'maxResponseBytes', 'maxTokens']);
|
|
14
15
|
export const KEY_ENVS = { jev: 'TYPESAFE_API_KEY', 'openai-decisions': 'OPENAI_API_KEY',
|
|
@@ -56,7 +57,14 @@ export function validateConfig(input, { complete = false } = {}) {
|
|
|
56
57
|
check(config.module === undefined || ['custom-decision', 'custom-generative'].includes(config.provider),
|
|
57
58
|
'module requires custom-decision or custom-generative.', 'CONFIGURATION_ERROR');
|
|
58
59
|
check(config.options === undefined || isModule(config), 'options requires a custom provider module.', 'CONFIGURATION_ERROR');
|
|
60
|
+
// Validate nested shape even when a partial file omits its provider selector.
|
|
61
|
+
if (Object.hasOwn(config, 'dangerZone')) config.dangerZone = jevDangerZone(config.dangerZone);
|
|
59
62
|
if (config.provider !== undefined) {
|
|
63
|
+
if (config.provider !== 'jev') check(!Object.hasOwn(config, 'dangerZone'),
|
|
64
|
+
'dangerZone requires Jev.', 'CONFIGURATION_ERROR');
|
|
65
|
+
else {
|
|
66
|
+
jevRequestLimit(config.maxRequestBytes);
|
|
67
|
+
}
|
|
60
68
|
if (config.provider !== 'cloudflare-decisions') check(['accountId', 'rejectIfBusy', 'gateway'].every(key => config[key] === undefined),
|
|
61
69
|
'Cloudflare settings require cloudflare-decisions.', 'CONFIGURATION_ERROR');
|
|
62
70
|
else {
|
package/src/cli/config.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { readFile, stat } from 'node:fs/promises';
|
|
2
2
|
import { resolve, dirname } from 'node:path';
|
|
3
3
|
import { parseEnv } from 'node:util';
|
|
4
|
+
import { OPENAI_DEFAULT_MODEL } from '../providers/openai-profile.mjs';
|
|
4
5
|
import { DecisionError, requireCondition as check } from '../errors.mjs';
|
|
5
6
|
import { validateConfig, NUMERIC, PROVIDER_FIELDS } from './config-validation.mjs';
|
|
7
|
+
import { jevDangerZone } from '../providers/jev-profile.mjs';
|
|
6
8
|
export { validateConfig } from './config-validation.mjs';
|
|
7
9
|
export { configuredProvider } from './provider-loader.mjs';
|
|
8
10
|
|
|
@@ -11,6 +13,7 @@ const ENV_FIELDS = {
|
|
|
11
13
|
DECISION_API_KEY_ENV: 'apiKeyEnv', DECISION_TIMEOUT_MS: 'timeoutMs',
|
|
12
14
|
DECISION_MAX_RETRIES: 'maxRetries', DECISION_RESPONSE_FORMAT: 'responseFormat',
|
|
13
15
|
DECISION_ACCOUNT_ID: 'accountId', DECISION_REJECT_IF_BUSY: 'rejectIfBusy', DECISION_GATEWAY_ID: 'gateway',
|
|
16
|
+
DECISION_JEV_IMAGES: 'jevImages', DECISION_JEV_LONG_CONTEXT: 'jevLongContext',
|
|
14
17
|
};
|
|
15
18
|
|
|
16
19
|
async function boundedFile(path, maxBytes, label) {
|
|
@@ -46,13 +49,17 @@ export async function loadConfig({ configPath, envFile, overrides = {}, cwd = pr
|
|
|
46
49
|
fileConfig = validateConfig(fileConfig);
|
|
47
50
|
moduleBase = dirname(path);
|
|
48
51
|
}
|
|
49
|
-
const environment =
|
|
52
|
+
const environment = Object.create(null);
|
|
50
53
|
for (const [key, field] of Object.entries(ENV_FIELDS)) {
|
|
51
54
|
if (effectiveEnv[key] === undefined) continue;
|
|
52
55
|
const value = effectiveEnv[key];
|
|
53
56
|
if (NUMERIC.has(field)) check(typeof value === 'string' && /^\d+$/.test(value),
|
|
54
57
|
'Numeric DECISION_* variables must contain decimal integers.', 'CONFIGURATION_ERROR');
|
|
55
|
-
if (field === '
|
|
58
|
+
if (field === 'jevImages' || field === 'jevLongContext') {
|
|
59
|
+
check(value === 'true' || value === 'false', 'Jev DECISION_* flags must be true or false.', 'CONFIGURATION_ERROR');
|
|
60
|
+
environment.dangerZone = { ...(Object.hasOwn(environment, 'dangerZone') ? environment.dangerZone : {}),
|
|
61
|
+
[field === 'jevImages' ? 'images' : 'longContext']: value === 'true' };
|
|
62
|
+
} else if (field === 'rejectIfBusy') {
|
|
56
63
|
check(value === 'true' || value === 'false', 'DECISION_REJECT_IF_BUSY must be true or false.', 'CONFIGURATION_ERROR');
|
|
57
64
|
environment[field] = value === 'true';
|
|
58
65
|
} else environment[field] = field === 'gateway' ? { id: value } : NUMERIC.has(field) ? Number(value) : value;
|
|
@@ -61,6 +68,10 @@ export async function loadConfig({ configPath, envFile, overrides = {}, cwd = pr
|
|
|
61
68
|
// environment-selected provider must not inherit that provider's key/endpoint.
|
|
62
69
|
function mergeProviderLayer(base, layer, layerModuleBase = moduleBase) {
|
|
63
70
|
const next = { ...base };
|
|
71
|
+
const dangerZone = Object.hasOwn(layer, 'dangerZone') ? layer.dangerZone : undefined;
|
|
72
|
+
// Validate before spreading, without filling defaults that would erase a
|
|
73
|
+
// different opt-in from a lower layer.
|
|
74
|
+
if (dangerZone !== undefined) jevDangerZone(dangerZone);
|
|
64
75
|
// File and flag paths have different bases. Equal path text can identify
|
|
65
76
|
// different implementations; compare destinations before changing the base.
|
|
66
77
|
const moduleChanged = layer.module !== undefined && (typeof layer.module !== 'string' ||
|
|
@@ -71,6 +82,9 @@ export async function loadConfig({ configPath, envFile, overrides = {}, cwd = pr
|
|
|
71
82
|
if (changed) for (const key of PROVIDER_FIELDS) delete next[key];
|
|
72
83
|
if (layer.module !== undefined) moduleBase = layerModuleBase;
|
|
73
84
|
return { ...next, ...layer,
|
|
85
|
+
...(dangerZone === undefined ? {} : { dangerZone: {
|
|
86
|
+
...(Object.hasOwn(next, 'dangerZone') ? next.dangerZone : {}), ...dangerZone,
|
|
87
|
+
} }),
|
|
74
88
|
...(layer.gateway === undefined ? {} : { gateway: { ...next.gateway, ...layer.gateway } }) };
|
|
75
89
|
}
|
|
76
90
|
const config = validateConfig(mergeProviderLayer(mergeProviderLayer(
|
|
@@ -78,7 +92,7 @@ export async function loadConfig({ configPath, envFile, overrides = {}, cwd = pr
|
|
|
78
92
|
if (config.provider === 'jev' && config.model === undefined && effectiveEnv.JEV_MODEL?.trim()) {
|
|
79
93
|
config.model = effectiveEnv.JEV_MODEL.trim();
|
|
80
94
|
}
|
|
81
|
-
if (config.provider === 'openai-decisions' && config.model === undefined) config.model =
|
|
95
|
+
if (config.provider === 'openai-decisions' && config.model === undefined) config.model = OPENAI_DEFAULT_MODEL;
|
|
82
96
|
if (config.provider === 'cloudflare-decisions' && config.model === undefined) config.model = 'clef';
|
|
83
97
|
return { config, env: effectiveEnv, moduleBase };
|
|
84
98
|
}
|
package/src/cli/decisions.mjs
CHANGED
|
@@ -3,11 +3,21 @@ import { encodeCommonDecision } from '../providers/decisions-codec.mjs';
|
|
|
3
3
|
import { preflightCloudflareDecision, preflightCommonCloudflareDecision } from '../providers/cloudflare-preflight.mjs';
|
|
4
4
|
import { encodeCloudflareDecision } from '../providers/cloudflare-codec.mjs';
|
|
5
5
|
import { CLOUDFLARE_MAX_REQUEST_LIMIT } from '../providers/cloudflare-profile.mjs';
|
|
6
|
+
import { resolveOpenAIModel } from '../providers/openai-profile.mjs';
|
|
7
|
+
import { preflightCommonJevDecision, preflightJevDecision, encodeJevDecision } from '../providers/jev-preflight.mjs';
|
|
8
|
+
import { JEV_REQUEST_LIMIT } from '../providers/jev-profile.mjs';
|
|
6
9
|
|
|
7
10
|
/** Provider-specific native dispatch is shared by validation, initialization and calls. */
|
|
8
11
|
export function decisionProfile(provider) {
|
|
12
|
+
if (provider === 'jev') return {
|
|
13
|
+
common: preflightCommonJevDecision, native: preflightJevDecision, encode: encodeJevDecision,
|
|
14
|
+
models: true, inputLimit: JEV_REQUEST_LIMIT,
|
|
15
|
+
options: config => ({ model: config.model, maxRequestBytes: config.maxRequestBytes,
|
|
16
|
+
dangerZone: Object.hasOwn(config, 'dangerZone') ? config.dangerZone : undefined }),
|
|
17
|
+
};
|
|
9
18
|
if (provider === 'openai-decisions') return {
|
|
10
19
|
common: preflightCommonDecision, native: preflightDecision, encode: encodeCommonDecision,
|
|
20
|
+
resolveModel: resolveOpenAIModel,
|
|
11
21
|
options: config => ({ model: config.model, maxRequestBytes: config.maxRequestBytes }),
|
|
12
22
|
};
|
|
13
23
|
if (provider === 'cloudflare-decisions') return {
|
package/src/cli/initialize.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { DecisionError } from '../errors.mjs';
|
|
|
4
4
|
import { validateRequest } from '../validation.mjs';
|
|
5
5
|
import { decisionProfile } from './decisions.mjs';
|
|
6
6
|
import { validateConfig, KEY_ENVS } from './config-validation.mjs';
|
|
7
|
+
import { OPENAI_DEFAULT_MODEL } from '../providers/openai-profile.mjs';
|
|
7
8
|
|
|
8
9
|
/** Templates are honest edit points for an unknown protocol, never fake inference. */
|
|
9
10
|
function providerTemplate(kind) {
|
|
@@ -38,6 +39,7 @@ export async function initialize(directory, config, native = false, moduleBase =
|
|
|
38
39
|
...(generateModule ? { module: './provider.mjs' } : {}),
|
|
39
40
|
...(config.module ? { module: resolve(moduleBase, config.module) } : {}),
|
|
40
41
|
...(config.provider === 'jev' && config.model === undefined ? { model: 'jev-latest' } : {}),
|
|
42
|
+
...(config.provider === 'openai-decisions' && config.model === undefined ? { model: OPENAI_DEFAULT_MODEL } : {}),
|
|
41
43
|
...(custom && config.model === undefined ? { model: 'YOUR_MODEL' } : {}),
|
|
42
44
|
}, { complete: true });
|
|
43
45
|
const request = { state: "ModuleNotFoundError: No module named 'pytest'", questions: {
|
|
@@ -47,12 +49,16 @@ export async function initialize(directory, config, native = false, moduleBase =
|
|
|
47
49
|
missing_dependency: { type: 'noul', instructions: 'Does this indicate a missing Python package?' },
|
|
48
50
|
} };
|
|
49
51
|
const profile = decisionProfile(config.provider);
|
|
52
|
+
const sample = native ? profile.native(profile.encode(validateRequest(request, template.model)),
|
|
53
|
+
profile.options(template)) : request;
|
|
54
|
+
// OpenAI request files inherit selection from configuration, including after later edits.
|
|
55
|
+
const generated = { ...sample };
|
|
56
|
+
if (config.provider === 'openai-decisions') delete generated.model;
|
|
50
57
|
const schema = await readFile(new URL('../../schemas/decision.config.schema.json', import.meta.url), 'utf8');
|
|
51
58
|
const files = {
|
|
52
59
|
'decision.config.json': JSON.stringify(template, null, 2) + '\n',
|
|
53
60
|
'decision.config.schema.json': schema,
|
|
54
|
-
'request.json': JSON.stringify(
|
|
55
|
-
profile.options(template)) : request, null, 2) + '\n',
|
|
61
|
+
'request.json': JSON.stringify(generated, null, 2) + '\n',
|
|
56
62
|
'.env.example': `${template.apiKeyEnv}=replace_with_your_api_key\n` +
|
|
57
63
|
(config.provider === 'cloudflare-decisions' ? 'CLOUDFLARE_ACCOUNT_ID=replace_with_your_account_id\n' : ''),
|
|
58
64
|
'.gitignore': '.env\n.env.*\n!.env.example\nnode_modules/\n*.tmp\n',
|