@fayegram-ai/decision-bridge 0.1.0 → 0.1.1
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 +4 -0
- package/README.md +18 -12
- package/docs/README.md +3 -3
- package/docs/development/ARCHITECTURE.md +22 -1
- package/docs/development/DESIGN.md +11 -0
- package/docs/development/TESTING.md +23 -1
- 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/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 +14 -3
- package/docs/reference/CONFIGURATION.md +6 -4
- package/docs/reference/ERRORS.md +5 -2
- package/docs/reference/PROVIDERS.md +8 -5
- package/docs/reference/SDK.md +9 -3
- package/examples/configs/openai-decisions.json +1 -1
- package/examples/decisions.native.json +1 -1
- package/package.json +1 -1
- package/src/capabilities.mjs +3 -6
- package/src/cli/config.mjs +2 -1
- package/src/cli/decisions.mjs +2 -0
- package/src/cli/initialize.mjs +8 -2
- package/src/cli/main.mjs +1 -0
- package/src/decisions.d.mts +5 -0
- package/src/index.d.mts +6 -3
- package/src/providers/decisions-preflight.mjs +18 -7
- package/src/providers/decisions-validation.mjs +3 -2
- package/src/providers/openai-decisions.mjs +17 -10
- package/src/providers/openai-profile.mjs +24 -0
- package/src/version.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Notable user-facing changes by version.
|
|
4
4
|
|
|
5
|
+
## 0.1.1
|
|
6
|
+
|
|
7
|
+
- Add OpenAI `default`/`luna` selectors and requested/resolved model metadata; improve CLI configuration and documentation.
|
|
8
|
+
|
|
5
9
|
## 0.1.0
|
|
6
10
|
|
|
7
11
|
Initial release of Decision Bridge as `@fayegram-ai/decision-bridge`.
|
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ embedded WAV/MP3 audio and MP4/WebM video, ordered images → audio → videos
|
|
|
29
29
|
The common API checks provider/model capabilities before dispatch; refusals are
|
|
30
30
|
errors.
|
|
31
31
|
|
|
32
|
-
**Version 0.1.
|
|
32
|
+
**Version 0.1.1 · Node.js 22.16+ or modern browsers (SDK) · ESM · zero runtime dependencies**
|
|
33
33
|
|
|
34
34
|
The npm package is
|
|
35
35
|
[@fayegram-ai/decision-bridge](https://www.npmjs.com/package/@fayegram-ai/decision-bridge).
|
|
@@ -41,24 +41,30 @@ This is an independent project. The custom generative presets identify their
|
|
|
41
41
|
probabilities as elicited model outputs.
|
|
42
42
|
Changing providers does **not** make their accuracy or calibration equivalent.
|
|
43
43
|
|
|
44
|
-
##
|
|
44
|
+
## Model selectors
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
Use these model selectors with the built-in decision providers:
|
|
47
47
|
|
|
48
|
-
| Decision provider | Model
|
|
49
|
-
| --- | --- | --- |
|
|
50
|
-
| Jev through TypeSafe (`jev`) | `jev-latest` |
|
|
51
|
-
| OpenAI Decisions (`openai-decisions`) | `
|
|
52
|
-
| Cloudflare (`cloudflare-decisions`) | `clef
|
|
53
|
-
| Cloudflare (`cloudflare-decisions`) | `clef-flash
|
|
54
|
-
| Cloudflare (`cloudflare-decisions`) | `clef-omni
|
|
48
|
+
| Decision provider | Model selectors | Provider default |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| Jev through TypeSafe (`jev`) | `jev-latest` | `jev-latest` |
|
|
51
|
+
| OpenAI Decisions (`openai-decisions`) | `default`, `luna` | `default` |
|
|
52
|
+
| Cloudflare (`cloudflare-decisions`) | `clef`, `@cf/cloudflare/clef` | `clef` |
|
|
53
|
+
| Cloudflare (`cloudflare-decisions`) | `clef-flash`, `@cf/cloudflare/clef-flash` | — |
|
|
54
|
+
| Cloudflare (`cloudflare-decisions`) | `clef-omni`, `@cf/cloudflare/clef-omni` | — |
|
|
55
55
|
|
|
56
56
|
`jev-latest` is Jev's default alias. Jev also accepts explicit model IDs available through your TypeSafe account;
|
|
57
57
|
use `bridge.listModels()` or the CLI `models` command to inspect that catalog.
|
|
58
58
|
The bridge does not maintain a static list of Jev versions. A moving alias or a
|
|
59
59
|
catalog entry does not guarantee inference access or reproducible model quality.
|
|
60
|
-
OpenAI Decisions
|
|
61
|
-
|
|
60
|
+
For OpenAI Decisions, `default` selects the provider default and `luna` selects
|
|
61
|
+
the supported Luna-family model. Decision Bridge resolves selectors to supported
|
|
62
|
+
upstream model IDs before calling the service. Mappings are fixed by the installed
|
|
63
|
+
Decision Bridge release. Explicit supported model IDs can also be selected; see
|
|
64
|
+
the [OpenAI model selection guide](docs/guides/OPENAI_DECISIONS.md#model-selection)
|
|
65
|
+
for details. Results retain the selected value in `meta.requestedModel`,
|
|
66
|
+
the outgoing ID in `meta.resolvedModel`, and the service-reported model in `model`.
|
|
67
|
+
Cloudflare accepts only the three models above, in short or full form.
|
|
62
68
|
Model discovery is unsupported for OpenAI Decisions and Cloudflare.
|
|
63
69
|
|
|
64
70
|
Additional decision protocols use [custom decision providers](docs/guides/CUSTOM_DECISION.md).
|
package/docs/README.md
CHANGED
|
@@ -7,8 +7,8 @@ Install `@fayegram-ai/decision-bridge` with npm; the
|
|
|
7
7
|
[getting started guide](guides/GETTING_STARTED.md#install-into-your-application)
|
|
8
8
|
also covers installation from a verified archive.
|
|
9
9
|
The hosted playground is [Decision Playground](https://decision-pg.fayegram.com/).
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
See [model selectors and provider defaults](../README.md#model-selectors) for
|
|
11
|
+
built-in decision models and the separate custom generative presets.
|
|
12
12
|
|
|
13
13
|
Decision Bridge is a JavaScript SDK with an optional command runner. It evaluates
|
|
14
14
|
supplied `state` or ordered `input`, plus `questions`, with an explicitly selected
|
|
@@ -27,7 +27,7 @@ the OpenAI and Cloudflare guides cover both.
|
|
|
27
27
|
| [Browser integration](guides/BROWSER.md) | Direct browser use, CORS requirements, and credential exposure |
|
|
28
28
|
| [Platforms](guides/PLATFORMS.md) | Windows, macOS, and Linux setup |
|
|
29
29
|
| [Switching models](guides/SWITCHING_MODELS.md) | Backend and model selection |
|
|
30
|
-
| [OpenAI Decisions](guides/OPENAI_DECISIONS.md) |
|
|
30
|
+
| [OpenAI Decisions](guides/OPENAI_DECISIONS.md) | Model selectors and upstream targets, selection metadata, common/native access, ordered images, and typed values |
|
|
31
31
|
| [Cloudflare Clef / Clef-flash / Clef-omni](guides/CLOUDFLARE_DECISIONS.md) | Common/native decisions, embedded images and Omni audio/video, REST and Workers AI binding, busy-mode compatibility, and optional Gateway |
|
|
32
32
|
| [Customization](guides/CUSTOMIZATION.md) | Questions, templates, extensions, and policies |
|
|
33
33
|
| [Custom providers](guides/CUSTOM_PROVIDERS.md) | Common classified contract and explicit CLI module loading |
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Decision Bridge exposes one application contract for structured decisions. The
|
|
6
6
|
bridge owns request snapshots, common validation, extension sequencing, the total
|
|
7
7
|
deadline, final result validation, immutable results, and bounded observations.
|
|
8
|
-
A
|
|
8
|
+
A provider owns its API encoding, transport, decoding, model selection,
|
|
9
9
|
capabilities, and native operations. Multiple models sharing a protocol can use
|
|
10
10
|
one provider implementation.
|
|
11
11
|
|
|
@@ -46,6 +46,26 @@ shared evaluation deadline and extension lifecycle.
|
|
|
46
46
|
Application policy decides whether a downstream action is permitted. The SDK
|
|
47
47
|
returns decisions without executing actions or maintaining an agent loop.
|
|
48
48
|
|
|
49
|
+
## Model selection and request preparation
|
|
50
|
+
|
|
51
|
+
The bridge applies the provider's configured default when a request omits `model`.
|
|
52
|
+
Its validated request and extension context retain that selected string. A provider
|
|
53
|
+
may resolve a selection to an upstream ID while preparing its outgoing request;
|
|
54
|
+
the shared provider contract has no selector-resolution hook.
|
|
55
|
+
|
|
56
|
+
OpenAI's `openai-profile.mjs` owns the `default` selection, accepted selectors,
|
|
57
|
+
upstream targets, and capabilities. Common and native preparation retain the
|
|
58
|
+
validated selection separately from the resolved native request. Preflight returns
|
|
59
|
+
the selected request, while serialization and byte-limit checks use the resolved
|
|
60
|
+
request. Common evaluation and native `createDecision()` share dispatch, which
|
|
61
|
+
sends only that resolved request.
|
|
62
|
+
|
|
63
|
+
Successful OpenAI calls report the selected string in `meta.requestedModel` and
|
|
64
|
+
the outgoing ID in `meta.resolvedModel`. The response's `model` retains the
|
|
65
|
+
service-reported identity. CLI offline validation and execution use the same
|
|
66
|
+
preflight, and generated OpenAI requests inherit selection from configuration.
|
|
67
|
+
See [OpenAI model selection](../guides/OPENAI_DECISIONS.md#model-selection).
|
|
68
|
+
|
|
49
69
|
## Module map
|
|
50
70
|
|
|
51
71
|
| Module | Responsibility |
|
|
@@ -61,6 +81,7 @@ returns decisions without executing actions or maintaining an agent loop.
|
|
|
61
81
|
| `src/custom/generative/` | Opt-in protocol presets, schema, prompts, and result derivation |
|
|
62
82
|
| `schemas/decision.config.schema.json` | Shipped CLI configuration structure and editor support |
|
|
63
83
|
| `src/providers/` | API orchestration, model profiles, preflight, codecs, bounded image/audio/video inspection, and transports |
|
|
84
|
+
| `src/providers/openai-profile.mjs`, `src/providers/decisions-preflight.mjs` | Release-owned OpenAI selection, capabilities, and preparation of selected and outgoing requests |
|
|
64
85
|
| `src/embedded.mjs` | Shared embedded data URL and canonical base64 checks |
|
|
65
86
|
| `src/transport.mjs`, `src/response-transport.mjs` | Credential destinations, bounded bodies, safe errors, retries |
|
|
66
87
|
| `src/async.mjs` | Deadlines and cancellation races |
|
|
@@ -43,6 +43,17 @@ Read JSON config only when named. Load custom ESM only when explicitly selected.
|
|
|
43
43
|
Protect credential destinations and expose bounded callbacks. Consequence: plugins
|
|
44
44
|
are powerful and must be trusted; cancellation is not a JavaScript sandbox.
|
|
45
45
|
|
|
46
|
+
## Release-owned model selectors
|
|
47
|
+
|
|
48
|
+
OpenAI Decisions offers `default` for the provider's default supported model and
|
|
49
|
+
`luna` for the supported Luna-family model, alongside explicit supported upstream
|
|
50
|
+
IDs. The installed package fixes the target and capability profile for each
|
|
51
|
+
selector. The provider resolves it locally before serialization; no discovery,
|
|
52
|
+
automatic upgrade, or fallback occurs during a call. Consequence: applications can
|
|
53
|
+
use a stable selection string while inspecting selected, outgoing, and reported
|
|
54
|
+
model identities separately. A future target change requires an intentional
|
|
55
|
+
package update and verification of that model's protocol and capabilities.
|
|
56
|
+
|
|
46
57
|
## Conservative retries and independent observations
|
|
47
58
|
|
|
48
59
|
Retry selected explicit transient HTTP statuses within a deadline. Do not retry
|
|
@@ -42,7 +42,7 @@ as evidence that every environment passed.
|
|
|
42
42
|
- `transport.test.mjs`: HTTP policy, limits, retries, cancellation, and deadlines.
|
|
43
43
|
- `deadlines.test.mjs`: synchronous preparation/dispatch/completion and inherited budgets.
|
|
44
44
|
- `result-validation.test.mjs`: provider-selected precision and policy-hook boundaries.
|
|
45
|
-
- `decisions*.test.mjs`: native/common OpenAI codecs, boundaries, preflight, CLI, and smoke-helper fixtures.
|
|
45
|
+
- `decisions*.test.mjs`: native/common OpenAI codecs, selectors and model identities, boundaries, preflight, CLI, and smoke-helper fixtures.
|
|
46
46
|
- `cloudflare*.test.mjs`: models, images, codecs, REST/binding/Gateway arguments, errors, and CLI limits.
|
|
47
47
|
- `custom-providers.test.mjs`, `custom-config.test.mjs`: classified SDK paths, provenance, presets/modules, schema and initialization.
|
|
48
48
|
- `generative-helpers.test.mjs`: exported prompt/normalization helper contracts and unsupported input rejection.
|
|
@@ -63,6 +63,28 @@ All model responses used by ordinary tests are synthetic. HTTP integration serve
|
|
|
63
63
|
listen on loopback. Tests do not require account keys or make paid model calls.
|
|
64
64
|
Test workspaces are temporary.
|
|
65
65
|
|
|
66
|
+
### OpenAI model-selector coverage
|
|
67
|
+
|
|
68
|
+
For changes to OpenAI selection or preparation, run the focused suites from the
|
|
69
|
+
source root:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
node --test test/decisions-selectors.test.mjs test/decisions-preflight.test.mjs test/decisions-cli.test.mjs
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
These cover omitted, `default`, `luna`, and explicit upstream selection; unsupported
|
|
76
|
+
and malformed values; independently selected, outgoing, and reported identities;
|
|
77
|
+
capabilities and extension context; immutable input; SDK batch preflight; resolved
|
|
78
|
+
wire-byte limits; CLI precedence and mixed-selector rows; and generated requests
|
|
79
|
+
that inherit later configuration changes. Existing Decisions suites also cover
|
|
80
|
+
revalidation after input preparation, refusals, retries, and deadlines.
|
|
81
|
+
|
|
82
|
+
Type and installed-package checks exercise metadata declarations, SDK/CLI
|
|
83
|
+
selection, and browser execution without Node globals. Use `npm run verify` for
|
|
84
|
+
the complete integration check. A selector mapping change must be verified against
|
|
85
|
+
the target model's protocol and capabilities; local fixtures alone do not establish
|
|
86
|
+
compatibility with a newly selected upstream model.
|
|
87
|
+
|
|
66
88
|
## Optional authenticated provider smoke check
|
|
67
89
|
|
|
68
90
|
Live inference is explicitly opted in and can incur charges. Configure credentials
|
|
@@ -4,8 +4,8 @@ Use `cloudflare-decisions` for hosted inference with `clef` (the default),
|
|
|
4
4
|
`clef-flash`, or `clef-omni`. All three support common bridge requests and the
|
|
5
5
|
dedicated native API. Each accepts its full `@cf/cloudflare/<model>` alias.
|
|
6
6
|
The provider normalizes the endpoint identifier and the required input selector together.
|
|
7
|
-
See the
|
|
8
|
-
short selectors and full
|
|
7
|
+
See the [model selector inventory](../../README.md#model-selectors) for supported
|
|
8
|
+
short selectors and full forms.
|
|
9
9
|
|
|
10
10
|
| Model | Hosted context, in tokens | Default client request budget | Media |
|
|
11
11
|
| --- | ---: | ---: | --- |
|
|
@@ -17,7 +17,7 @@ extension path.
|
|
|
17
17
|
`Provider` requires a nonempty `name`, explicit `kind: 'decision' | 'generative'`,
|
|
18
18
|
and `evaluate(request, { signal? })`. Optional fields and hooks are:
|
|
19
19
|
|
|
20
|
-
- `defaultModel`: a nonempty
|
|
20
|
+
- `defaultModel`: a nonempty selection string, otherwise the request must supply one.
|
|
21
21
|
- `probabilitySource`: known provenance; generative providers use `elicited`.
|
|
22
22
|
- `getCapabilities(model)`: a pure synchronous capability profile.
|
|
23
23
|
- `validateRequest(request)`: pure synchronous provider preflight; no inference.
|
|
@@ -31,7 +31,9 @@ that implement this contract directly. Missing or invalid kinds fail at construc
|
|
|
31
31
|
`bridge.providerKind` and `result.meta.providerKind` come from the contract and
|
|
32
32
|
cannot be overridden by response metadata.
|
|
33
33
|
|
|
34
|
-
The request is already snapshotted, validated,
|
|
34
|
+
The request is already snapshotted, validated, supplied with an effective model
|
|
35
|
+
selection, and frozen. Its model string retains the selection; any mapping to an
|
|
36
|
+
upstream ID belongs to the provider's preparation and transport.
|
|
35
37
|
Return canonical `{ model, answers, usage, meta? }`; the bridge validates and
|
|
36
38
|
freezes the result. Custom providers own API encoding, transport, response mapping,
|
|
37
39
|
credential destinations, bounded bodies, safe errors, and cancellation cleanup.
|
|
@@ -25,7 +25,7 @@ You can also install a verified local archive, replacing the path below. See
|
|
|
25
25
|
a source checkout:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
npm install /path/to/fayegram-ai-decision-bridge-0.1.
|
|
28
|
+
npm install /path/to/fayegram-ai-decision-bridge-0.1.1.tgz
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
The installed package contains the SDK, declarations, command runner, examples,
|
|
@@ -1,13 +1,50 @@
|
|
|
1
1
|
# OpenAI Decisions
|
|
2
2
|
|
|
3
|
-
The `openai-decisions` provider implements the native `POST /v1/decisions` API
|
|
4
|
-
|
|
3
|
+
The `openai-decisions` provider implements the native `POST /v1/decisions` API.
|
|
4
|
+
Its default model selector is `default`; `luna` selects the supported Luna-family
|
|
5
|
+
model, and `gpt-6-luna` selects that explicit upstream ID. Decision Bridge resolves
|
|
6
|
+
all three selections to `gpt-6-luna` in this release before calling the service.
|
|
5
7
|
This integration follows the official [Decisions guide](https://developers.openai.com/api/docs/guides/decisions)
|
|
6
8
|
and [API reference](https://developers.openai.com/api/reference/resources/decisions/methods/create).
|
|
7
9
|
Account access and remote limits still apply. Unknown models fail locally with
|
|
8
10
|
`UNSUPPORTED_CAPABILITY`. The opt-in `openai-compatible` preset in `custom/generative` continues
|
|
9
11
|
using Chat Completions.
|
|
10
12
|
|
|
13
|
+
## Model selection
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
import { createProvider } from '@fayegram-ai/decision-bridge';
|
|
17
|
+
|
|
18
|
+
createProvider({ type: 'openai-decisions' }); // Selects default.
|
|
19
|
+
createProvider({ type: 'openai-decisions', model: 'luna' });
|
|
20
|
+
createProvider({ type: 'openai-decisions', model: 'gpt-6-luna' });
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
These are alternative configurations. An omitted request `model` inherits the
|
|
24
|
+
provider's configured selection; an explicit request `model` overrides it.
|
|
25
|
+
With no provider or request model configured, the selection is `default`:
|
|
26
|
+
|
|
27
|
+
| Selection | Selected value | Current upstream target |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| Omitted, with no configured model | `default` | `gpt-6-luna` |
|
|
30
|
+
| `default` | `default` | `gpt-6-luna` |
|
|
31
|
+
| `luna` | `luna` | `gpt-6-luna` |
|
|
32
|
+
| `gpt-6-luna` | `gpt-6-luna` | `gpt-6-luna` |
|
|
33
|
+
|
|
34
|
+
Mappings are fixed by the installed Decision Bridge release; there is
|
|
35
|
+
no runtime model discovery, automatic upgrading, or fallback after failure.
|
|
36
|
+
The `default` selector follows the provider default, while `luna` stays within
|
|
37
|
+
the Luna family. The resolved upstream model must have a supported capability
|
|
38
|
+
profile before a selector can target it.
|
|
39
|
+
|
|
40
|
+
`provider.defaultModel` and `bridge.defaultModel` expose the configured selection,
|
|
41
|
+
using `default` when provider configuration omits `model`. Validated requests and
|
|
42
|
+
extension context retain the effective selected value. Successful common
|
|
43
|
+
and native calls expose `meta.requestedModel` for that selection and
|
|
44
|
+
`meta.resolvedModel` for the upstream ID sent. The common `result.model` and native
|
|
45
|
+
`decision.model` preserve the service-reported identity, which may differ from
|
|
46
|
+
the outgoing ID. Byte limits apply to the resolved wire request.
|
|
47
|
+
|
|
11
48
|
## Common bridge access
|
|
12
49
|
|
|
13
50
|
Set `OPENAI_API_KEY` in the process environment:
|
|
@@ -141,14 +178,15 @@ const { decision, meta } = await provider.createDecision({
|
|
|
141
178
|
}, { signal: controller.signal });
|
|
142
179
|
```
|
|
143
180
|
|
|
144
|
-
An omitted model
|
|
181
|
+
An omitted model inherits the provider's configured selection. Native input accepts text or
|
|
145
182
|
multiple user messages containing text or ordered `input_text`/`input_image`
|
|
146
183
|
parts. Only user roles are supported. Files, external image URLs, audio, tools,
|
|
147
184
|
and item references are rejected locally. Exported `NativeDecisionRequest` and
|
|
148
185
|
related types describe this format.
|
|
149
186
|
|
|
150
187
|
`decision` preserves native answer arrays, optional question names, probabilities,
|
|
151
|
-
confidence, and full usage. `meta`
|
|
188
|
+
confidence, and full usage. `meta` has the exported `OpenAIDecisionsTransportMetadata`
|
|
189
|
+
shape: requested/resolved model identities, transport duration, attempt count,
|
|
152
190
|
and request ID when available. Results are immutable. This method bypasses
|
|
153
191
|
common bridge extensions.
|
|
154
192
|
|
|
@@ -197,6 +235,10 @@ provider. Native evaluation/validation/batch require an explicit file or `-`.
|
|
|
197
235
|
Mode is never inferred from JSON. Initialization writes a native request, which
|
|
198
236
|
must still be evaluated with `--native`. Offline validation checks built-in
|
|
199
237
|
capabilities without credentials; custom capabilities are checked at evaluation.
|
|
238
|
+
OpenAI initialization stores the model selection in configuration and omits it
|
|
239
|
+
from the generated request so later configuration changes apply. The default
|
|
240
|
+
configuration uses `default`. Offline validation reports the selected value as
|
|
241
|
+
`model` and the outgoing upstream ID as `resolvedModel`.
|
|
200
242
|
Decisions offline validation also checks native field limits and the configured
|
|
201
243
|
request byte limit in both modes.
|
|
202
244
|
|
package/docs/guides/PLATFORMS.md
CHANGED
|
@@ -22,7 +22,7 @@ compatible native Node runtime. See [testing](../development/TESTING.md).
|
|
|
22
22
|
Open PowerShell at the cloned repository or extracted source directory:
|
|
23
23
|
|
|
24
24
|
```powershell
|
|
25
|
-
cd "C:\path\to\decision-bridge-0.1.
|
|
25
|
+
cd "C:\path\to\decision-bridge-0.1.1"
|
|
26
26
|
node --version
|
|
27
27
|
node bin/decision.mjs validate examples/request.json
|
|
28
28
|
Copy-Item .env.example .env
|
|
@@ -48,7 +48,7 @@ the archive rather than reusing generated `node_modules` or executable shims.
|
|
|
48
48
|
## macOS / Linux
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
|
-
cd "/path/to/decision-bridge-0.1.
|
|
51
|
+
cd "/path/to/decision-bridge-0.1.1"
|
|
52
52
|
node --version
|
|
53
53
|
node bin/decision.mjs validate examples/request.json
|
|
54
54
|
cp .env.example .env
|
|
@@ -4,7 +4,7 @@ The bridge's request contains `questions`, either `state` or ordered `input`, an
|
|
|
4
4
|
optionally `model`. Ordered input and richer question types require provider capabilities.
|
|
5
5
|
The provider translates it into that service's wire protocol and validates the
|
|
6
6
|
response. Select a provider when constructing your SDK bridge.
|
|
7
|
-
|
|
7
|
+
See [model selectors and provider defaults](../../README.md#model-selectors).
|
|
8
8
|
|
|
9
9
|
## Native and custom selection
|
|
10
10
|
|
|
@@ -72,7 +72,7 @@ example and provide that backend's credential in `.env`:
|
|
|
72
72
|
| Native SDK `createProvider` type | Credential variable | Model selection |
|
|
73
73
|
| --- | --- | --- |
|
|
74
74
|
| `jev` | `TYPESAFE_API_KEY` | Defaults to `jev-latest`; an explicit model overrides it |
|
|
75
|
-
| `openai-decisions` | `OPENAI_API_KEY` | Defaults to `gpt-6-luna` |
|
|
75
|
+
| `openai-decisions` | `OPENAI_API_KEY` | Defaults to `default`; also accepts `luna` and explicit `gpt-6-luna` |
|
|
76
76
|
| `cloudflare-decisions` | `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` for REST | Defaults to `clef`; also supports `clef-flash` and `clef-omni` |
|
|
77
77
|
|
|
78
78
|
Custom generative presets use `createGenerativeProvider` instead. Their types are
|
|
@@ -83,7 +83,7 @@ using the following native alternatives:
|
|
|
83
83
|
|
|
84
84
|
```javascript
|
|
85
85
|
createProvider({ type: 'jev', model: 'jev-latest' });
|
|
86
|
-
createProvider({ type: 'openai-decisions', model: '
|
|
86
|
+
createProvider({ type: 'openai-decisions', model: 'luna' });
|
|
87
87
|
createProvider({ type: 'cloudflare-decisions', model: 'clef-flash' });
|
|
88
88
|
createGenerativeProvider({ type: 'openai-compatible', model: process.env.DECISION_MODEL });
|
|
89
89
|
createGenerativeProvider({ type: 'anthropic', model: process.env.DECISION_MODEL });
|
|
@@ -96,6 +96,12 @@ The SDK does not automatically read `DECISION_*` settings: this example passes
|
|
|
96
96
|
`process.env.DECISION_MODEL` explicitly. The SDK factory property is **`type`**;
|
|
97
97
|
a command-runner JSON profile uses **`provider`**.
|
|
98
98
|
|
|
99
|
+
OpenAI's `default` selector chooses the provider default and `luna` chooses the
|
|
100
|
+
supported Luna-family model. Both currently resolve to upstream `gpt-6-luna`
|
|
101
|
+
through Decision Bridge. Use `model: 'gpt-6-luna'` to select that explicit upstream
|
|
102
|
+
ID. Mappings are fixed by the installed package release; results expose the
|
|
103
|
+
selection in `meta.requestedModel` and outgoing ID in `meta.resolvedModel`.
|
|
104
|
+
|
|
99
105
|
For Jev and the generative providers, list models with `await bridge.listModels()`.
|
|
100
106
|
OpenAI Decisions and Cloudflare Clef do not implement model discovery and return
|
|
101
107
|
`UNSUPPORTED_OPERATION`; their capability profiles do not prove account availability.
|
|
@@ -143,7 +149,7 @@ uppercase model placeholders with IDs available to your account/server:
|
|
|
143
149
|
|
|
144
150
|
```bash
|
|
145
151
|
npm exec -- decision-bridge evaluate ./my-decisions/request.json --provider jev --model jev-latest --env-file .env
|
|
146
|
-
npm exec -- decision-bridge evaluate ./my-decisions/request.json --provider openai-decisions --model
|
|
152
|
+
npm exec -- decision-bridge evaluate ./my-decisions/request.json --provider openai-decisions --model default --env-file .env
|
|
147
153
|
npm exec -- decision-bridge evaluate ./my-decisions/request.json --provider cloudflare-decisions --model clef-flash --env-file .env
|
|
148
154
|
npm exec -- decision-bridge evaluate ./my-decisions/request.json --provider custom-generative --preset openrouter --model VENDOR/MODEL_ID --env-file .env
|
|
149
155
|
npm exec -- decision-bridge evaluate ./my-decisions/request.json --provider custom-generative --preset openai-compatible --model MODEL_ID --env-file .env
|
|
@@ -18,6 +18,20 @@ explicit config. A compatible base includes its version prefix, not the final
|
|
|
18
18
|
`/chat/completions` path. Jev and the custom Anthropic preset require origins
|
|
19
19
|
without a path. See [configuration](../reference/CONFIGURATION.md#precedence).
|
|
20
20
|
|
|
21
|
+
## OpenAI reports a versioned model after I select default or luna
|
|
22
|
+
|
|
23
|
+
`default` and `luna` are Decision Bridge model selectors. Both currently resolve
|
|
24
|
+
to `gpt-6-luna`, which is the upstream ID sent to OpenAI. Successful common and
|
|
25
|
+
native calls expose the selection as `meta.requestedModel` and the outgoing ID
|
|
26
|
+
as `meta.resolvedModel`. `result.model` or native `decision.model` preserves the
|
|
27
|
+
service-reported identity, which may differ from the outgoing ID.
|
|
28
|
+
|
|
29
|
+
An omitted request model inherits the configured model. OpenAI initialization
|
|
30
|
+
omits `model` from its generated requests so later configuration edits apply.
|
|
31
|
+
If selection fails with `UNSUPPORTED_CAPABILITY`, check the
|
|
32
|
+
[accepted selectors](OPENAI_DECISIONS.md#model-selection); the bridge does not
|
|
33
|
+
discover another model or fall back after failure.
|
|
34
|
+
|
|
21
35
|
## Structured output is rejected
|
|
22
36
|
|
|
23
37
|
Check that the selected endpoint/model supports the requested `json_schema` mode.
|
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
|
|
|
@@ -71,6 +78,10 @@ explicit service implementation error until you connect it. An existing
|
|
|
71
78
|
`--provider-module` is referenced without execution or overwrite. All profiles
|
|
72
79
|
include a copied JSON Schema for editor validation. See [custom providers](../guides/CUSTOM_PROVIDERS.md).
|
|
73
80
|
|
|
81
|
+
OpenAI initialization writes the selected model into configuration, using `default`
|
|
82
|
+
when omitted. Its generated common and native requests omit `model`, allowing
|
|
83
|
+
later configuration changes to control selection.
|
|
84
|
+
|
|
74
85
|
## Flags
|
|
75
86
|
|
|
76
87
|
| Flag | Meaning |
|
|
@@ -80,7 +91,7 @@ include a copied JSON Schema for editor validation. See [custom providers](../gu
|
|
|
80
91
|
| `--provider NAME` | `jev`, `openai-decisions`, `cloudflare-decisions`, `custom-decision`, `custom-generative` |
|
|
81
92
|
| `--preset NAME` | custom-generative preset: `openai-compatible`, `openrouter`, or `anthropic`; exclusive with module |
|
|
82
93
|
| `--native` | Explicit native Decisions input/output; init/evaluate/validate/batch only |
|
|
83
|
-
| `--model
|
|
94
|
+
| `--model SELECTOR` | Model selector or supported upstream ID; overrides input JSON model and configured default |
|
|
84
95
|
| `--base-url URL` | Custom endpoint; requires explicit credential selection |
|
|
85
96
|
| `--api-key-env NAME` | Environment variable name, never the key itself |
|
|
86
97
|
| `--account-id ID` | Cloudflare REST account ID |
|
|
@@ -40,7 +40,7 @@ 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 |
|
|
@@ -88,8 +88,8 @@ existing process environment > --env-file values
|
|
|
88
88
|
The loader does not mutate the process environment. The SDK independently reads
|
|
89
89
|
its documented ambient credential variables at provider construction.
|
|
90
90
|
|
|
91
|
-
The [
|
|
92
|
-
selectors
|
|
91
|
+
The [model selector list](../../README.md#model-selectors) records native
|
|
92
|
+
selectors and defaults. Configuration precedence does not expand a
|
|
93
93
|
provider's accepted models or grant account access.
|
|
94
94
|
|
|
95
95
|
For evaluation model selection:
|
|
@@ -99,7 +99,9 @@ For evaluation model selection:
|
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
Jev defaults to `jev-latest`, with `JEV_MODEL` as a legacy/direct convenience.
|
|
102
|
-
Native OpenAI Decisions defaults to `
|
|
102
|
+
Native OpenAI Decisions defaults to `default` and accepts `luna` or explicit
|
|
103
|
+
`gpt-6-luna`; Decision Bridge currently resolves each to upstream `gpt-6-luna`.
|
|
104
|
+
OpenAI accepts transport settings
|
|
103
105
|
and rejects generative output settings. Its default retry count is zero. Cloudflare Clef defaults to `clef`, uses
|
|
104
106
|
account/token credentials, rejects generation settings, and also defaults to zero retries.
|
|
105
107
|
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`.
|
package/docs/reference/SDK.md
CHANGED
|
@@ -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
|
|
@@ -207,7 +212,8 @@ estimated token count is fabricated.
|
|
|
207
212
|
| --- | --- |
|
|
208
213
|
| `provider` | Bridge backend name; native Jev reports `typesafe` |
|
|
209
214
|
| `providerKind` | `decision` or `generative`; authoritative provider classification, never response-selected |
|
|
210
|
-
| `requestedModel` |
|
|
215
|
+
| `requestedModel` | Selected model after applying the provider default; OpenAI retains `default`, `luna`, or the explicit ID |
|
|
216
|
+
| `resolvedModel` | Upstream model ID sent by OpenAI Decisions; optional for other providers |
|
|
211
217
|
| `probabilitySource` | `native`, `elicited`, or custom/`unspecified` provenance |
|
|
212
218
|
| `confidenceSource` | `native` or `unavailable` for built-in providers |
|
|
213
219
|
| `modelSource` | `reported` or `requested` fallback for built-in providers |
|
package/package.json
CHANGED
package/src/capabilities.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
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';
|
|
4
5
|
|
|
5
6
|
/** Conservative defaults preserve the pre-existing custom-provider contract. */
|
|
6
7
|
export const LEGACY_CAPABILITIES = freezeJson({
|
|
@@ -9,11 +10,6 @@ export const LEGACY_CAPABILITIES = freezeJson({
|
|
|
9
10
|
maxImages: 0, maxChoiceOptions: 255, maxScoreLevels: 10,
|
|
10
11
|
audio: false, videos: false, maxAudioClips: 0, maxVideos: 0,
|
|
11
12
|
});
|
|
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,
|
|
16
|
-
});
|
|
17
13
|
|
|
18
14
|
const CLOUDFLARE_CAPABILITIES = freezeJson({
|
|
19
15
|
supported: true, orderedInput: true, images: true, typedChoices: true,
|
|
@@ -29,7 +25,8 @@ export function builtInCapabilities(type, model) {
|
|
|
29
25
|
maxAudioClips: profile?.omni ? 4 : 0, maxVideos: profile?.omni ? 2 : 0 });
|
|
30
26
|
}
|
|
31
27
|
if (type !== 'openai-decisions') return LEGACY_CAPABILITIES;
|
|
32
|
-
|
|
28
|
+
const profile = openaiModelProfile(model);
|
|
29
|
+
return freezeJson({ ...LEGACY_CAPABILITIES, ...profile, supported: !!profile });
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
export function providerCapabilities(provider, model) {
|
package/src/cli/config.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
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';
|
|
6
7
|
export { validateConfig } from './config-validation.mjs';
|
|
@@ -78,7 +79,7 @@ export async function loadConfig({ configPath, envFile, overrides = {}, cwd = pr
|
|
|
78
79
|
if (config.provider === 'jev' && config.model === undefined && effectiveEnv.JEV_MODEL?.trim()) {
|
|
79
80
|
config.model = effectiveEnv.JEV_MODEL.trim();
|
|
80
81
|
}
|
|
81
|
-
if (config.provider === 'openai-decisions' && config.model === undefined) config.model =
|
|
82
|
+
if (config.provider === 'openai-decisions' && config.model === undefined) config.model = OPENAI_DEFAULT_MODEL;
|
|
82
83
|
if (config.provider === 'cloudflare-decisions' && config.model === undefined) config.model = 'clef';
|
|
83
84
|
return { config, env: effectiveEnv, moduleBase };
|
|
84
85
|
}
|
package/src/cli/decisions.mjs
CHANGED
|
@@ -3,11 +3,13 @@ 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';
|
|
6
7
|
|
|
7
8
|
/** Provider-specific native dispatch is shared by validation, initialization and calls. */
|
|
8
9
|
export function decisionProfile(provider) {
|
|
9
10
|
if (provider === 'openai-decisions') return {
|
|
10
11
|
common: preflightCommonDecision, native: preflightDecision, encode: encodeCommonDecision,
|
|
12
|
+
resolveModel: resolveOpenAIModel,
|
|
11
13
|
options: config => ({ model: config.model, maxRequestBytes: config.maxRequestBytes }),
|
|
12
14
|
};
|
|
13
15
|
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',
|
package/src/cli/main.mjs
CHANGED
|
@@ -214,6 +214,7 @@ export async function runCli(argv, { stdin = process.stdin, stdout = process.std
|
|
|
214
214
|
? (await import('../custom/generative/schema.mjs')).buildResponseSchema : undefined;
|
|
215
215
|
await writeJson(command === 'schema' ? buildResponseSchema(request.questions)
|
|
216
216
|
: { valid: true, validationScope: isModule(loaded.config) ? 'structure' : 'provider', questionCount: Object.keys(request.questions).length, model: selectedModel,
|
|
217
|
+
...(profile?.resolveModel ? { resolvedModel: profile.resolveModel(request.model) } : {}),
|
|
217
218
|
...(values.native ? { native: true } : {}) });
|
|
218
219
|
} else {
|
|
219
220
|
let request;
|
package/src/decisions.d.mts
CHANGED
|
@@ -39,6 +39,11 @@ export interface OpenAIDecisionResponse {
|
|
|
39
39
|
export interface TransportMetadata {
|
|
40
40
|
readonly attempts: number; readonly durationMs: number; readonly requestId?: string;
|
|
41
41
|
}
|
|
42
|
+
/** Selected model, resolved outgoing ID, and transport diagnostics for native calls. */
|
|
43
|
+
export interface OpenAIDecisionsTransportMetadata extends TransportMetadata {
|
|
44
|
+
readonly requestedModel: string;
|
|
45
|
+
readonly resolvedModel: string;
|
|
46
|
+
}
|
|
42
47
|
export type OpenAIDecisionsMetadata = {
|
|
43
48
|
readonly usage: OpenAIDecisionUsage;
|
|
44
49
|
readonly answers: Readonly<Record<string, { readonly type: 'predicate' | 'choice' | 'score'; readonly confidence?: number }>>;
|
package/src/index.d.mts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/** JSON values are read-only because the bridge snapshots/freezes its boundaries. */
|
|
2
2
|
import type { NativeDecisionRequest, OpenAIDecisionResponse, OpenAIDecisionUsage,
|
|
3
|
-
OpenAIDecisionsMetadata,
|
|
3
|
+
OpenAIDecisionsMetadata, OpenAIDecisionsTransportMetadata } from './decisions.d.mts';
|
|
4
4
|
export type { NativeDecisionInputPart, NativeDecisionMessage, NativeDecisionQuestion, NativeDecisionRequest,
|
|
5
|
-
NativeDecisionAnswer, OpenAIDecisionResponse, OpenAIDecisionUsage, OpenAIDecisionsMetadata,
|
|
5
|
+
NativeDecisionAnswer, OpenAIDecisionResponse, OpenAIDecisionUsage, OpenAIDecisionsMetadata,
|
|
6
|
+
OpenAIDecisionsTransportMetadata, TransportMetadata } from './decisions.d.mts';
|
|
6
7
|
import type { CloudflareDecisionsOptions, CloudflareDecisionRequest, CloudflareDecisionResponse,
|
|
7
8
|
CloudflareTransportMetadata, CloudflareDecisionsMetadata } from './cloudflare-decisions.d.mts';
|
|
8
9
|
export type { CloudflareDecisionModel, CloudflareDecisionImage, CloudflareDecisionAudio, CloudflareDecisionVideo,
|
|
@@ -88,6 +89,8 @@ export interface ProviderResult<Q extends Questions = Questions> {
|
|
|
88
89
|
}
|
|
89
90
|
export interface Metadata {
|
|
90
91
|
readonly provider: string; readonly providerKind: ProviderKind; readonly requestedModel: string;
|
|
92
|
+
/** Upstream model ID sent after provider selection, when supplied by the provider. */
|
|
93
|
+
readonly resolvedModel?: string;
|
|
91
94
|
readonly probabilitySource: 'native' | 'elicited' | 'unspecified' | (string & {});
|
|
92
95
|
readonly confidenceSource?: 'native' | 'unavailable' | (string & {});
|
|
93
96
|
readonly modelSource?: 'reported' | 'requested';
|
|
@@ -171,7 +174,7 @@ export class OpenAIDecisionsProvider implements DecisionProvider {
|
|
|
171
174
|
validateRequest(request: ResolvedRequest): void;
|
|
172
175
|
evaluate<const Q extends Questions>(request: EvaluationRequest<Q>, options?: ProviderCallOptions): Promise<ProviderResult<Q>>;
|
|
173
176
|
createDecision(request: NativeDecisionRequest, options?: ProviderCallOptions):
|
|
174
|
-
Promise<{ readonly decision: OpenAIDecisionResponse; readonly meta:
|
|
177
|
+
Promise<{ readonly decision: OpenAIDecisionResponse; readonly meta: OpenAIDecisionsTransportMetadata }>;
|
|
175
178
|
}
|
|
176
179
|
export class CloudflareDecisionsProvider implements DecisionProvider {
|
|
177
180
|
readonly kind: 'decision'; readonly name: 'cloudflare-decisions'; readonly probabilitySource: 'native'; readonly defaultModel: string;
|
|
@@ -4,17 +4,28 @@ import { assertCapabilities, builtInCapabilities } from '../capabilities.mjs';
|
|
|
4
4
|
import { serializeRequestBody } from '../transport.mjs';
|
|
5
5
|
import { validateDecisionRequest } from './decisions-validation.mjs';
|
|
6
6
|
import { encodeCommonDecision } from './decisions-codec.mjs';
|
|
7
|
+
import { OPENAI_DEFAULT_MODEL, resolveOpenAIModel } from './openai-profile.mjs';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
/** Keep the caller selection separate from the resolved, size-checked wire request. */
|
|
10
|
+
export function prepareDecision(input, { model = OPENAI_DEFAULT_MODEL, maxRequestBytes } = {}) {
|
|
9
11
|
const request = validateDecisionRequest(input, model);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
const native = freezeJson({ ...request, model: resolveOpenAIModel(request.model) });
|
|
13
|
+
serializeRequestBody(native, maxRequestBytes);
|
|
14
|
+
return { request, native };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function preflightDecision(input, options = {}) {
|
|
18
|
+
return prepareDecision(input, options).request;
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
/** Check converted JSON text and wire bytes, not just the common request shape. */
|
|
15
|
-
export function
|
|
16
|
-
const request = validateRequest(input, options.model ??
|
|
22
|
+
export function prepareCommonDecision(input, options = {}) {
|
|
23
|
+
const request = validateRequest(input, options.model ?? OPENAI_DEFAULT_MODEL);
|
|
17
24
|
assertCapabilities(request, builtInCapabilities('openai-decisions', request.model));
|
|
18
|
-
|
|
19
|
-
return freezeJson(request);
|
|
25
|
+
const { native } = prepareDecision(encodeCommonDecision(request), options);
|
|
26
|
+
return { request: freezeJson(request), native };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function preflightCommonDecision(input, options = {}) {
|
|
30
|
+
return prepareCommonDecision(input, options).request;
|
|
20
31
|
}
|
|
@@ -2,6 +2,7 @@ import { DecisionError, requireCondition as check } from '../errors.mjs';
|
|
|
2
2
|
import { cloneJson, freezeJson, isRecord } from '../validation.mjs';
|
|
3
3
|
import { isTextWithinLimit, validateImage } from '../input.mjs';
|
|
4
4
|
import { builtInCapabilities } from '../capabilities.mjs';
|
|
5
|
+
import { OPENAI_DEFAULT_MODEL } from './openai-profile.mjs';
|
|
5
6
|
|
|
6
7
|
const own = (value, key) => Object.hasOwn(value, key);
|
|
7
8
|
function fields(value, allowed, required, label, code = 'VALIDATION_ERROR') {
|
|
@@ -12,8 +13,8 @@ function text(value, label, max = 1048576) {
|
|
|
12
13
|
check(isTextWithinLimit(value, max), `Invalid ${label}.`);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
/** Validate
|
|
16
|
-
export function validateDecisionRequest(input, defaultModel =
|
|
16
|
+
/** Validate native fields while retaining the selected model for preparation and metadata. */
|
|
17
|
+
export function validateDecisionRequest(input, defaultModel = OPENAI_DEFAULT_MODEL) {
|
|
17
18
|
const request = cloneJson(input, 'native decision request');
|
|
18
19
|
fields(request, ['input', 'model', 'questions', 'safety_identifier'], ['input', 'questions'], 'decision request');
|
|
19
20
|
const model = request.model ?? defaultModel;
|
|
@@ -5,8 +5,9 @@ import { builtInCapabilities } from '../capabilities.mjs';
|
|
|
5
5
|
import { JsonTransport, parseBaseURL, validateApiKey } from '../transport.mjs';
|
|
6
6
|
import { environmentValue } from '../runtime-env.mjs';
|
|
7
7
|
import { validateDecisionResponse } from './decisions-validation.mjs';
|
|
8
|
-
import {
|
|
9
|
-
import { preflightCommonDecision,
|
|
8
|
+
import { decodeCommonDecision } from './decisions-codec.mjs';
|
|
9
|
+
import { preflightCommonDecision, prepareCommonDecision, prepareDecision } from './decisions-preflight.mjs';
|
|
10
|
+
import { OPENAI_DEFAULT_MODEL } from './openai-profile.mjs';
|
|
10
11
|
|
|
11
12
|
/** Native Decisions transport. Common and dedicated access share a single codec. */
|
|
12
13
|
export class OpenAIDecisionsProvider {
|
|
@@ -17,7 +18,7 @@ export class OpenAIDecisionsProvider {
|
|
|
17
18
|
#maxRequestBytes;
|
|
18
19
|
|
|
19
20
|
constructor(options = {}) {
|
|
20
|
-
const { apiKey, model =
|
|
21
|
+
const { apiKey, model = OPENAI_DEFAULT_MODEL, baseURL = 'https://api.openai.com', maxRetries = 0, ...transportOptions } =
|
|
21
22
|
optionsObject(options, ['apiKey', 'model', 'baseURL', ...TRANSPORT_FIELDS], 'OpenAI Decisions options');
|
|
22
23
|
const endpoint = parseBaseURL(baseURL, { originOnly: true });
|
|
23
24
|
const supplied = apiKey === undefined && endpoint.origin === 'https://api.openai.com'
|
|
@@ -36,16 +37,22 @@ export class OpenAIDecisionsProvider {
|
|
|
36
37
|
preflightCommonDecision(request, { model: this.defaultModel, maxRequestBytes: this.#maxRequestBytes });
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const { data, meta } = await this.#transport.request('POST', '/v1/decisions',
|
|
42
|
-
const
|
|
40
|
+
/** Dispatch only prepared wire input; keep selection and reported identity independent. */
|
|
41
|
+
async #perform({ request, native }, options) {
|
|
42
|
+
const { data, meta: transport } = await this.#transport.request('POST', '/v1/decisions', native, options);
|
|
43
|
+
const meta = { ...transport, requestedModel: request.model, resolvedModel: native.model };
|
|
44
|
+
const decision = withResponseMeta(meta, () => validateDecisionResponse(data, native));
|
|
43
45
|
return freezeJson({ decision, meta });
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
async createDecision(input, options = {}) {
|
|
49
|
+
const prepared = prepareDecision(input, { model: this.defaultModel, maxRequestBytes: this.#maxRequestBytes });
|
|
50
|
+
return this.#perform(prepared, options);
|
|
51
|
+
}
|
|
52
|
+
|
|
46
53
|
async evaluate(input, options = {}) {
|
|
47
|
-
const
|
|
48
|
-
const { decision, meta } = await this
|
|
49
|
-
return freezeJson(withResponseMeta(meta, () => decodeCommonDecision(decision, request, meta)));
|
|
54
|
+
const prepared = prepareCommonDecision(input, { model: this.defaultModel, maxRequestBytes: this.#maxRequestBytes });
|
|
55
|
+
const { decision, meta } = await this.#perform(prepared, options);
|
|
56
|
+
return freezeJson(withResponseMeta(meta, () => decodeCommonDecision(decision, prepared.request, meta)));
|
|
50
57
|
}
|
|
51
58
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { requireCondition as check } from '../errors.mjs';
|
|
2
|
+
|
|
3
|
+
/** Release-owned selectors resolve locally; upstream responses never change this mapping. */
|
|
4
|
+
export const OPENAI_DEFAULT_MODEL = 'default';
|
|
5
|
+
const LUNA_MODEL = 'gpt-6-luna';
|
|
6
|
+
const SELECTORS = Object.freeze({ default: LUNA_MODEL, luna: LUNA_MODEL, [LUNA_MODEL]: LUNA_MODEL });
|
|
7
|
+
const PROFILES = Object.freeze({
|
|
8
|
+
[LUNA_MODEL]: Object.freeze({
|
|
9
|
+
supported: true, orderedInput: true, images: true, typedChoices: true,
|
|
10
|
+
labeledScores: true, safetyIdentifier: true,
|
|
11
|
+
maxImages: 128, maxChoiceOptions: 255, maxScoreLevels: null,
|
|
12
|
+
}),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/** Lookup does not throw for unsupported selections, matching capability-query semantics. */
|
|
16
|
+
export function openaiModelProfile(selector) {
|
|
17
|
+
return typeof selector === 'string' && Object.hasOwn(SELECTORS, selector)
|
|
18
|
+
? PROFILES[SELECTORS[selector]] : undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function resolveOpenAIModel(selector) {
|
|
22
|
+
check(openaiModelProfile(selector), 'Selected provider/model does not support this model.', 'UNSUPPORTED_CAPABILITY');
|
|
23
|
+
return SELECTORS[selector];
|
|
24
|
+
}
|
package/src/version.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Keep in sync with package.json; scripts/check.mjs verifies the release version. */
|
|
2
|
-
export const VERSION = '0.1.
|
|
2
|
+
export const VERSION = '0.1.1';
|