@fayegram-ai/decision-bridge 0.0.0-stage → 0.1.0
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/.env.example +10 -0
- package/CHANGELOG.md +75 -0
- package/CONTRIBUTING.md +57 -0
- package/LICENSE +21 -0
- package/README.md +307 -2
- package/SECURITY.md +88 -0
- package/bin/decision.mjs +15 -0
- package/docs/README.md +58 -0
- package/docs/development/ARCHITECTURE.md +118 -0
- package/docs/development/DESIGN.md +66 -0
- package/docs/development/README.md +12 -0
- package/docs/development/RELEASING.md +100 -0
- package/docs/development/REPOSITORY_LAYOUT.md +33 -0
- package/docs/development/TESTING.md +131 -0
- package/docs/guides/BROWSER.md +43 -0
- package/docs/guides/CLOUDFLARE_DECISIONS.md +374 -0
- package/docs/guides/CUSTOMIZATION.md +141 -0
- package/docs/guides/CUSTOM_DECISION.md +66 -0
- package/docs/guides/CUSTOM_GENERATIVE.md +164 -0
- package/docs/guides/CUSTOM_PROVIDERS.md +81 -0
- package/docs/guides/GETTING_STARTED.md +252 -0
- package/docs/guides/OPENAI_DECISIONS.md +210 -0
- package/docs/guides/PLATFORMS.md +107 -0
- package/docs/guides/SWITCHING_MODELS.md +202 -0
- package/docs/guides/TROUBLESHOOTING.md +82 -0
- package/docs/reference/CLI.md +202 -0
- package/docs/reference/CONFIGURATION.md +168 -0
- package/docs/reference/ERRORS.md +82 -0
- package/docs/reference/PROVIDERS.md +113 -0
- package/docs/reference/SDK.md +388 -0
- package/docs/reference/SOURCES.md +51 -0
- package/examples/basic.mjs +17 -0
- package/examples/batch.mjs +13 -0
- package/examples/cloudflare-decisions.mjs +22 -0
- package/examples/cloudflare-omni.mjs +32 -0
- package/examples/cloudflare-worker.mjs +21 -0
- package/examples/cloudflare.native.json +28 -0
- package/examples/configs/anthropic.json +9 -0
- package/examples/configs/cloudflare-decisions.json +8 -0
- package/examples/configs/custom-decision.json +9 -0
- package/examples/configs/custom-generative.json +14 -0
- package/examples/configs/jev.json +7 -0
- package/examples/configs/local.json +12 -0
- package/examples/configs/openai-decisions.json +7 -0
- package/examples/configs/openai.json +10 -0
- package/examples/configs/openrouter.json +10 -0
- package/examples/custom/decision-provider.mjs +15 -0
- package/examples/custom/generative-provider.mjs +13 -0
- package/examples/custom/openrouter.mjs +13 -0
- package/examples/customize.mjs +23 -0
- package/examples/decisions.native.json +19 -0
- package/examples/extended.mjs +43 -0
- package/examples/openai-decisions.mjs +20 -0
- package/examples/request.json +31 -0
- package/examples/requests.jsonl +2 -0
- package/package.json +87 -4
- package/schemas/decision.config.schema.json +553 -0
- package/src/async.mjs +63 -0
- package/src/batch.mjs +38 -0
- package/src/bridge.mjs +184 -0
- package/src/capabilities.mjs +95 -0
- package/src/cli/config-validation.mjs +89 -0
- package/src/cli/config.mjs +84 -0
- package/src/cli/decisions.mjs +19 -0
- package/src/cli/initialize.mjs +79 -0
- package/src/cli/io.mjs +171 -0
- package/src/cli/main.mjs +280 -0
- package/src/cli/ordered.mjs +43 -0
- package/src/cli/provider-loader.mjs +58 -0
- package/src/cloudflare-decisions.d.mts +78 -0
- package/src/custom/decision.d.mts +5 -0
- package/src/custom/decision.mjs +6 -0
- package/src/custom/definition.mjs +19 -0
- package/src/custom/generative/anthropic.mjs +75 -0
- package/src/custom/generative/index.d.mts +42 -0
- package/src/custom/generative/index.mjs +29 -0
- package/src/custom/generative/openai-compatible.mjs +101 -0
- package/src/custom/generative/schema.mjs +95 -0
- package/src/custom/generative/shared.mjs +39 -0
- package/src/decisions.d.mts +45 -0
- package/src/embedded.mjs +24 -0
- package/src/errors.mjs +49 -0
- package/src/index.d.mts +229 -0
- package/src/index.mjs +7 -0
- package/src/input.mjs +37 -0
- package/src/options.mjs +15 -0
- package/src/policy.mjs +44 -0
- package/src/probabilities.mjs +27 -0
- package/src/provider-contract.mjs +42 -0
- package/src/providers/cloudflare-audio.mjs +83 -0
- package/src/providers/cloudflare-codec.mjs +39 -0
- package/src/providers/cloudflare-decisions.mjs +81 -0
- package/src/providers/cloudflare-images.mjs +95 -0
- package/src/providers/cloudflare-media.mjs +36 -0
- package/src/providers/cloudflare-preflight.mjs +92 -0
- package/src/providers/cloudflare-profile.mjs +102 -0
- package/src/providers/cloudflare-transport.mjs +63 -0
- package/src/providers/cloudflare-validation.mjs +48 -0
- package/src/providers/cloudflare-video.mjs +84 -0
- package/src/providers/decisions-codec.mjs +61 -0
- package/src/providers/decisions-preflight.mjs +20 -0
- package/src/providers/decisions-validation.mjs +150 -0
- package/src/providers/index.mjs +26 -0
- package/src/providers/jev.mjs +51 -0
- package/src/providers/media-binary.mjs +51 -0
- package/src/providers/openai-decisions.mjs +51 -0
- package/src/questions.mjs +15 -0
- package/src/response-transport.mjs +176 -0
- package/src/runtime-env.mjs +4 -0
- package/src/transport.mjs +51 -0
- package/src/validation.mjs +245 -0
- package/src/version.mjs +2 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# OpenAI Decisions
|
|
2
|
+
|
|
3
|
+
The `openai-decisions` provider implements the native `POST /v1/decisions` API,
|
|
4
|
+
supporting `gpt-6-luna` as its default and only accepted model in this release.
|
|
5
|
+
This integration follows the official [Decisions guide](https://developers.openai.com/api/docs/guides/decisions)
|
|
6
|
+
and [API reference](https://developers.openai.com/api/reference/resources/decisions/methods/create).
|
|
7
|
+
Account access and remote limits still apply. Unknown models fail locally with
|
|
8
|
+
`UNSUPPORTED_CAPABILITY`. The opt-in `openai-compatible` preset in `custom/generative` continues
|
|
9
|
+
using Chat Completions.
|
|
10
|
+
|
|
11
|
+
## Common bridge access
|
|
12
|
+
|
|
13
|
+
Set `OPENAI_API_KEY` in the process environment:
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
import { createBridge, createProvider, choice, noul, scoreLevels } from '@fayegram-ai/decision-bridge';
|
|
17
|
+
|
|
18
|
+
const provider = createProvider({ type: 'openai-decisions' });
|
|
19
|
+
const bridge = createBridge({ provider });
|
|
20
|
+
const result = await bridge.evaluate({
|
|
21
|
+
state: { complaint: 'I was charged twice.' },
|
|
22
|
+
questions: {
|
|
23
|
+
route: choice('Route the complaint.', { billing: 'Payments.', other: null }),
|
|
24
|
+
duplicate: noul('Does this describe a duplicate charge?'),
|
|
25
|
+
impact: scoreLevels('Rate the impact.', [
|
|
26
|
+
{ label: 'Low', description: 'Small inconvenience.' },
|
|
27
|
+
{ label: 'High', description: 'A required task is blocked.' },
|
|
28
|
+
]),
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
console.log(result.answers.route.choice);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Existing state/question forms and answer shapes are retained. Strings remain
|
|
35
|
+
unchanged; structured state, instructions, and descriptions become JSON text.
|
|
36
|
+
Null choice descriptions are omitted. Legacy scores use zero-based native labels
|
|
37
|
+
and preserve the original rubric in their common legend.
|
|
38
|
+
|
|
39
|
+
`noul` without criteria, or with empty criteria, uses a native predicate. With
|
|
40
|
+
true/false descriptions, it uses a native boolean choice carrying both options
|
|
41
|
+
and the supplied descriptions; `noul` is the probability for boolean `true`.
|
|
42
|
+
That native choice's confidence is retained in provider metadata.
|
|
43
|
+
|
|
44
|
+
## Ordered text and images
|
|
45
|
+
|
|
46
|
+
Provide exactly one of `state` or `input`. An existing JSON array supplied as
|
|
47
|
+
`state` retains its original meaning. New `input` is a nonempty ordered array:
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
const result = await bridge.evaluate({
|
|
51
|
+
input: [
|
|
52
|
+
{ type: 'text', text: 'Assess this product photo.' },
|
|
53
|
+
{ type: 'image', dataUrl: imageDataUrl, detail: 'high' },
|
|
54
|
+
{ type: 'text', text: 'Consider only visible damage.' },
|
|
55
|
+
],
|
|
56
|
+
safetyIdentifier: 'opaque-application-user-id',
|
|
57
|
+
questions: { damaged: noul('Is visible damage present?') },
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Images must be nonempty inline base64 data URLs, such as
|
|
62
|
+
`data:image/png;base64,...`. The bridge does not fetch image URLs or read files.
|
|
63
|
+
Convert image bytes in your application. OpenAI permits at most 128 images.
|
|
64
|
+
Detail accepts `low`, `high`, `auto`, `original`, or null; omission uses the service
|
|
65
|
+
default. Text/image ordering is preserved.
|
|
66
|
+
|
|
67
|
+
The default transport request/response limits are 8 MiB each, including encoded
|
|
68
|
+
images. Set `maxRequestBytes` explicitly for larger requests, up to the existing
|
|
69
|
+
64 MiB ceiling. These local limits do not override service limits.
|
|
70
|
+
|
|
71
|
+
`safetyIdentifier` is an optional per-request opaque string of at most 128
|
|
72
|
+
Unicode characters, or null. It maps to `safety_identifier`; avoid private identity
|
|
73
|
+
data. Character limits count Unicode code points rather than UTF-16 code units.
|
|
74
|
+
|
|
75
|
+
## Typed choices and labelled scores
|
|
76
|
+
|
|
77
|
+
The array overload of `choice` accepts `{ value: string | boolean, description? }`:
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
const question = choice('Choose the matching outcome.', [
|
|
81
|
+
{ value: true, description: 'The condition is satisfied.' },
|
|
82
|
+
{ value: false, description: 'The condition is not satisfied.' },
|
|
83
|
+
{ value: 'unknown', description: 'Insufficient evidence.' },
|
|
84
|
+
]);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This question stores `options` instead of `criteria`. Its answer preserves the
|
|
88
|
+
typed `choice` and uses `probabilities: [{ value, probability }, ...]`. Boolean
|
|
89
|
+
`true` and string `"true"` are distinct; values must be unique. Numeric choices and
|
|
90
|
+
coercion to object keys are not supported. TypeScript preserves literal unions.
|
|
91
|
+
`decideChoice` supports these answers and typed abstention values.
|
|
92
|
+
|
|
93
|
+
`scoreLevels` stores ordered `levels` instead of `criteria` and requires at least
|
|
94
|
+
two `{ label, description? }` objects. Its answer retains the numeric score and
|
|
95
|
+
index-keyed probability map, with the original level objects in the legend.
|
|
96
|
+
OpenAI choice questions require 2–255 unique options in both common forms and
|
|
97
|
+
native requests. Labelled scores do not inherit Jev's 10-level limit; legacy
|
|
98
|
+
score criteria retain the 2–10-level limit. JSON snapshots and transport caps
|
|
99
|
+
apply to all requests.
|
|
100
|
+
|
|
101
|
+
## Capabilities and preparation
|
|
102
|
+
|
|
103
|
+
`bridge.getCapabilities(model?)` synchronously reports support and limits for
|
|
104
|
+
ordered input, images, typed choices, labelled scores, and safety identifiers.
|
|
105
|
+
`bridge.validate(request)` resolves the model, snapshots/freezes the request,
|
|
106
|
+
and checks capabilities without inference. Unsupported combinations fail before
|
|
107
|
+
dispatch with `UNSUPPORTED_CAPABILITY`.
|
|
108
|
+
|
|
109
|
+
Preflight also checks the encoded native field limits and the complete UTF-8 wire
|
|
110
|
+
body against `maxRequestBytes`. SDK array batches check every raw request before
|
|
111
|
+
dispatching any entry. Prepared input is revalidated during each evaluation.
|
|
112
|
+
|
|
113
|
+
Jev, OpenAI-compatible, OpenRouter, and Anthropic currently support the legacy
|
|
114
|
+
common forms. Existing custom providers default to that profile; implement
|
|
115
|
+
`getCapabilities(model)` and corresponding behavior to support new forms.
|
|
116
|
+
|
|
117
|
+
Use `prepareInput(parts, context)` for ordered input and `prepareState` for state.
|
|
118
|
+
A preparation extension must implement the hook for the form being used;
|
|
119
|
+
incompatible extensions fail explicitly rather than being skipped. Extensions
|
|
120
|
+
can implement both hooks. `onResult` works with either form and runs only after
|
|
121
|
+
success, with the existing isolated-failure policy.
|
|
122
|
+
|
|
123
|
+
Legacy common requests retain `meta.schemaVersion: 1`. New input/question forms
|
|
124
|
+
or safety identifiers use version 2.
|
|
125
|
+
|
|
126
|
+
## Dedicated native access
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
const { decision, meta } = await provider.createDecision({
|
|
130
|
+
input: 'The customer reports a broken screen.',
|
|
131
|
+
questions: [
|
|
132
|
+
{ type: 'predicate', name: 'damaged', instructions: 'Is damage reported?' },
|
|
133
|
+
{ type: 'choice', instructions: 'Choose a route.', choices: [
|
|
134
|
+
{ value: 'repair' }, { value: 'review' },
|
|
135
|
+
] },
|
|
136
|
+
{ type: 'score', name: 'impact', instructions: 'Rate the impact.', levels: [
|
|
137
|
+
{ label: 'Low' }, { label: 'High' },
|
|
138
|
+
] },
|
|
139
|
+
],
|
|
140
|
+
safety_identifier: 'opaque-application-user-id',
|
|
141
|
+
}, { signal: controller.signal });
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
An omitted model resolves to the provider default. Native input accepts text or
|
|
145
|
+
multiple user messages containing text or ordered `input_text`/`input_image`
|
|
146
|
+
parts. Only user roles are supported. Files, external image URLs, audio, tools,
|
|
147
|
+
and item references are rejected locally. Exported `NativeDecisionRequest` and
|
|
148
|
+
related types describe this format.
|
|
149
|
+
|
|
150
|
+
`decision` preserves native answer arrays, optional question names, probabilities,
|
|
151
|
+
confidence, and full usage. `meta` contains transport duration, attempt count,
|
|
152
|
+
and request ID when available. Results are immutable. This method bypasses
|
|
153
|
+
common bridge extensions.
|
|
154
|
+
|
|
155
|
+
## Refusals, results, and execution
|
|
156
|
+
|
|
157
|
+
Both paths fail the complete call with `MODEL_REFUSAL` if any question is refused.
|
|
158
|
+
They return no fabricated answer or partial success. The error retains validated
|
|
159
|
+
`usage`, `refusedQuestions: [{ index, name }]`, and available HTTP diagnostics;
|
|
160
|
+
it does not retain sibling answers or raw upstream bodies. JSONL batches mark
|
|
161
|
+
that request `ok: false`; independent rows can succeed.
|
|
162
|
+
|
|
163
|
+
Common confidence is preserved within its existing [0, 1] invariant. Native
|
|
164
|
+
access validates finite confidence without imposing that common range or an
|
|
165
|
+
undocumented argmax rule. Common validation rejects inconsistent responses
|
|
166
|
+
rather than repairing them. Provider confidence semantics are not assumed to
|
|
167
|
+
be equivalent or equally calibrated.
|
|
168
|
+
|
|
169
|
+
Common usage maps reported input/output and cache counts.
|
|
170
|
+
`meta.openaiDecisions.usage` preserves full native usage, including total and
|
|
171
|
+
reasoning tokens. `meta.openaiDecisions.answers` retains native answer types and
|
|
172
|
+
reported confidence. Predicate confidence and token counts are never invented.
|
|
173
|
+
|
|
174
|
+
Decisions defaults to **zero HTTP retries**. Explicit `maxRetries` opts into the
|
|
175
|
+
existing bounded policy and may repeat a billed request. There is no model
|
|
176
|
+
fallback, streaming, generation-parameter passthrough, or model listing.
|
|
177
|
+
`bridge.listModels()` fails with `UNSUPPORTED_OPERATION`.
|
|
178
|
+
|
|
179
|
+
Ambient `OPENAI_API_KEY` is used only for the default `https://api.openai.com`
|
|
180
|
+
origin. Custom origins require an explicit key; the CLI requires `apiKeyEnv`.
|
|
181
|
+
Supply an origin without a `/v1` base path. Authentication cannot be disabled.
|
|
182
|
+
Existing transport deadlines, cancellation, byte limits, and redaction apply.
|
|
183
|
+
|
|
184
|
+
## Command runner
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
decision-bridge evaluate request.json --provider openai-decisions --env-file .env
|
|
188
|
+
decision-bridge validate request.json --provider openai-decisions
|
|
189
|
+
decision-bridge init ./native-decisions --provider openai-decisions --native
|
|
190
|
+
decision-bridge validate ./native-decisions/request.json --provider openai-decisions --native
|
|
191
|
+
decision-bridge evaluate ./native-decisions/request.json --provider openai-decisions --native --env-file .env
|
|
192
|
+
decision-bridge batch native-requests.jsonl --provider openai-decisions --native --env-file .env
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`--native` applies only to `init`, `evaluate`, `validate`, and `batch` with this
|
|
196
|
+
provider. Native evaluation/validation/batch require an explicit file or `-`.
|
|
197
|
+
Mode is never inferred from JSON. Initialization writes a native request, which
|
|
198
|
+
must still be evaluated with `--native`. Offline validation checks built-in
|
|
199
|
+
capabilities without credentials; custom capabilities are checked at evaluation.
|
|
200
|
+
Decisions offline validation also checks native field limits and the configured
|
|
201
|
+
request byte limit in both modes.
|
|
202
|
+
|
|
203
|
+
The `schema` command remains a generative utility and rejects the new request
|
|
204
|
+
forms. Generation settings (`maxTokens`, `responseFormat`, `parameters`, etc.)
|
|
205
|
+
are rejected for Decisions. See the [CLI reference](../reference/CLI.md).
|
|
206
|
+
|
|
207
|
+
From a source checkout, the optional `npm run smoke:live -- --decisions --env-file .env`
|
|
208
|
+
checks four bounded requests when `RUN_LIVE_TESTS=1` is explicitly set. It checks
|
|
209
|
+
protocol behavior rather than prediction quality, disables retries, and prints
|
|
210
|
+
only model identities and token usage. Normal verification never runs it.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Platforms and installation
|
|
2
|
+
|
|
3
|
+
## Compatibility and verification
|
|
4
|
+
|
|
5
|
+
The SDK targets Node.js **22.16 or newer** on native Windows, macOS, and Linux,
|
|
6
|
+
and modern browsers through a browser application's module bundler. Its SDK
|
|
7
|
+
module graph uses standard web APIs and has no third-party runtime dependencies.
|
|
8
|
+
The optional command runner uses Node built-ins and remains Node-only. WSL and
|
|
9
|
+
Bash are not required, and no standalone `.exe` or `.app` is provided. Direct
|
|
10
|
+
browser calls also depend on [provider CORS and credential rules](BROWSER.md).
|
|
11
|
+
|
|
12
|
+
For application integration, follow the [SDK quickstart](GETTING_STARTED.md).
|
|
13
|
+
The shell-specific source commands below are for working in a source checkout.
|
|
14
|
+
|
|
15
|
+
The CI matrix targets Windows, macOS, and Linux with Node 22.16 and Node 24.
|
|
16
|
+
Consult run results for the exact commit and environment being evaluated;
|
|
17
|
+
the matrix itself is not a completed platform check. ARM machines require a
|
|
18
|
+
compatible native Node runtime. See [testing](../development/TESTING.md).
|
|
19
|
+
|
|
20
|
+
## Windows / PowerShell
|
|
21
|
+
|
|
22
|
+
Open PowerShell at the cloned repository or extracted source directory:
|
|
23
|
+
|
|
24
|
+
```powershell
|
|
25
|
+
cd "C:\path\to\decision-bridge-0.1.0"
|
|
26
|
+
node --version
|
|
27
|
+
node bin/decision.mjs validate examples/request.json
|
|
28
|
+
Copy-Item .env.example .env
|
|
29
|
+
notepad .env
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Replace the placeholder value for `TYPESAFE_API_KEY` in `.env` and save it locally.
|
|
33
|
+
The following file-based commands work without shell-specific continuation or
|
|
34
|
+
stdin encoding rules:
|
|
35
|
+
|
|
36
|
+
```powershell
|
|
37
|
+
node bin/decision.mjs models --env-file .env
|
|
38
|
+
node bin/decision.mjs evaluate examples/request.json --env-file .env
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`npm run models` and `npm run ask` are equivalent convenience commands. If a local
|
|
42
|
+
PowerShell execution policy blocks the npm PowerShell shim, use `npm.cmd test` /
|
|
43
|
+
`npm.cmd run ask`, or the direct `node` commands above. Do not weaken your system's
|
|
44
|
+
execution policy merely to run this project. Paths containing spaces should be
|
|
45
|
+
quoted. A project copied across operating systems should be re-extracted from
|
|
46
|
+
the archive rather than reusing generated `node_modules` or executable shims.
|
|
47
|
+
|
|
48
|
+
## macOS / Linux
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cd "/path/to/decision-bridge-0.1.0"
|
|
52
|
+
node --version
|
|
53
|
+
node bin/decision.mjs validate examples/request.json
|
|
54
|
+
cp .env.example .env
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Edit `.env` locally, then:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node bin/decision.mjs models --env-file .env
|
|
61
|
+
node bin/decision.mjs evaluate examples/request.json --env-file .env
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use a currently maintained, compatible Node release according to your organization’s
|
|
65
|
+
runtime policy; the minimum here describes this package's API requirements, not a
|
|
66
|
+
security recommendation to remain on an old patch release.
|
|
67
|
+
|
|
68
|
+
## Install in another project
|
|
69
|
+
|
|
70
|
+
Install the package with npm from your application's directory:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
npm install @fayegram-ai/decision-bridge
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For installation from a verified local archive, see
|
|
77
|
+
[getting started](GETTING_STARTED.md#install-into-your-application).
|
|
78
|
+
|
|
79
|
+
Import from `@fayegram-ai/decision-bridge` in ESM JavaScript, or invoke the installed
|
|
80
|
+
command runner through npm from your application directory:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
npm exec -- decision-bridge --version
|
|
84
|
+
npm exec -- decision-bridge init ./my-decisions
|
|
85
|
+
npm exec -- decision-bridge validate ./my-decisions/request.json
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
A CommonJS application can use `await import('@fayegram-ai/decision-bridge')` from an async
|
|
89
|
+
function; this package exports an ESM entry point, not a native `require()` entry.
|
|
90
|
+
The runtime package includes implementation, examples, type declarations and
|
|
91
|
+
maintained documentation. Use a source checkout for tests and CI. Node/npm and
|
|
92
|
+
the development TypeScript compiler are external prerequisites, not bundled
|
|
93
|
+
executables.
|
|
94
|
+
|
|
95
|
+
## Filesystems, streams, and limits
|
|
96
|
+
|
|
97
|
+
Output files use exclusive temporary files and no-clobber finalization by default.
|
|
98
|
+
Some filesystems do not support hard links; such no-clobber output fails explicitly
|
|
99
|
+
rather than falling back to overwriting. Unix mode 0600 is applied where supported;
|
|
100
|
+
on Windows, secure the working directory using your normal Windows permissions.
|
|
101
|
+
|
|
102
|
+
Input/output JSON is UTF-8. File arguments are the most portable way to avoid
|
|
103
|
+
shell pipeline encoding differences. A POSIX subprocess test covers SIGINT;
|
|
104
|
+
Windows console signal delivery still needs native validation.
|
|
105
|
+
Cancellation can stop waiting and close a blocked CLI output pipe, but cannot undo
|
|
106
|
+
an API request that already ran or force arbitrary custom-provider resources to
|
|
107
|
+
terminate. See [CLI I/O](../reference/CLI.md).
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Switching models without rewriting the application
|
|
2
|
+
|
|
3
|
+
The bridge's request contains `questions`, either `state` or ordered `input`, and
|
|
4
|
+
optionally `model`. Ordered input and richer question types require provider capabilities.
|
|
5
|
+
The provider translates it into that service's wire protocol and validates the
|
|
6
|
+
response. Select a provider when constructing your SDK bridge.
|
|
7
|
+
The root README lists [supported model selectors and aliases](../../README.md#supported-models).
|
|
8
|
+
|
|
9
|
+
## Native and custom selection
|
|
10
|
+
|
|
11
|
+
The main package supports native Jev, OpenAI Decisions, and Cloudflare. The
|
|
12
|
+
request remains shared when switching providers, subject to capabilities.
|
|
13
|
+
General-purpose models use the explicit [custom generative](CUSTOM_GENERATIVE.md)
|
|
14
|
+
entry point. A new decision protocol uses [custom decision](CUSTOM_DECISION.md).
|
|
15
|
+
The example below demonstrates the opt-in generative path; native alternatives
|
|
16
|
+
follow it.
|
|
17
|
+
|
|
18
|
+
## SDK: same request, different provider
|
|
19
|
+
|
|
20
|
+
Install the package as described in [getting started](GETTING_STARTED.md).
|
|
21
|
+
Create or update `.env` in your application directory with the selected backend's
|
|
22
|
+
key and an actual model ID available to your account. For OpenRouter:
|
|
23
|
+
|
|
24
|
+
```dotenv
|
|
25
|
+
OPENROUTER_API_KEY=replace_with_your_openrouter_key
|
|
26
|
+
DECISION_MODEL=VENDOR/MODEL_ID
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Keep `.env` out of source control. `VENDOR/MODEL_ID` is a placeholder to replace.
|
|
30
|
+
Save this complete example as `use-model.mjs` beside `.env`:
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
import { createBridge, choice, publicError } from '@fayegram-ai/decision-bridge';
|
|
34
|
+
import { createGenerativeProvider } from '@fayegram-ai/decision-bridge/custom/generative';
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const provider = createGenerativeProvider({
|
|
38
|
+
type: 'openrouter',
|
|
39
|
+
model: process.env.DECISION_MODEL,
|
|
40
|
+
responseFormat: 'json_schema',
|
|
41
|
+
});
|
|
42
|
+
const bridge = createBridge({ provider });
|
|
43
|
+
const result = await bridge.evaluate({
|
|
44
|
+
state: { message: 'I was charged twice for the same invoice.' },
|
|
45
|
+
questions: {
|
|
46
|
+
route: choice('Which team should inspect this message?', {
|
|
47
|
+
billing: 'Invoices, charges, or payment problems.',
|
|
48
|
+
technical: 'Software defects or connectivity problems.',
|
|
49
|
+
unknown: 'The evidence does not establish a team.',
|
|
50
|
+
}),
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
console.log(JSON.stringify(result, null, 2));
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(publicError(error));
|
|
56
|
+
process.exitCode = 1;
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Run it from your application directory:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
node --env-file=.env use-model.mjs
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
This performs a real evaluation and may incur charges. The request is defined in
|
|
67
|
+
this script; it does not need files from the bridge's source checkout.
|
|
68
|
+
|
|
69
|
+
To use another backend, replace the provider configuration in the
|
|
70
|
+
example and provide that backend's credential in `.env`:
|
|
71
|
+
|
|
72
|
+
| Native SDK `createProvider` type | Credential variable | Model selection |
|
|
73
|
+
| --- | --- | --- |
|
|
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` |
|
|
76
|
+
| `cloudflare-decisions` | `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` for REST | Defaults to `clef`; also supports `clef-flash` and `clef-omni` |
|
|
77
|
+
|
|
78
|
+
Custom generative presets use `createGenerativeProvider` instead. Their types are
|
|
79
|
+
`openai-compatible` (`OPENAI_API_KEY` at the default OpenAI origin), `openrouter`
|
|
80
|
+
(`OPENROUTER_API_KEY`), and `anthropic` (`ANTHROPIC_API_KEY`). Each requires a model
|
|
81
|
+
available to your account. Import the native `createProvider` from the root when
|
|
82
|
+
using the following native alternatives:
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
createProvider({ type: 'jev', model: 'jev-latest' });
|
|
86
|
+
createProvider({ type: 'openai-decisions', model: 'gpt-6-luna' });
|
|
87
|
+
createProvider({ type: 'cloudflare-decisions', model: 'clef-flash' });
|
|
88
|
+
createGenerativeProvider({ type: 'openai-compatible', model: process.env.DECISION_MODEL });
|
|
89
|
+
createGenerativeProvider({ type: 'anthropic', model: process.env.DECISION_MODEL });
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Each line is an alternative configuration. Constructors validate credentials but
|
|
93
|
+
make no inference calls. `evaluate()` sends the request. In the SDK, a request's
|
|
94
|
+
`model` overrides the provider default; omit it when the provider should choose.
|
|
95
|
+
The SDK does not automatically read `DECISION_*` settings: this example passes
|
|
96
|
+
`process.env.DECISION_MODEL` explicitly. The SDK factory property is **`type`**;
|
|
97
|
+
a command-runner JSON profile uses **`provider`**.
|
|
98
|
+
|
|
99
|
+
For Jev and the generative providers, list models with `await bridge.listModels()`.
|
|
100
|
+
OpenAI Decisions and Cloudflare Clef do not implement model discovery and return
|
|
101
|
+
`UNSUPPORTED_OPERATION`; their capability profiles do not prove account availability.
|
|
102
|
+
A catalog entry does not prove support for a particular response format. Check
|
|
103
|
+
that the selected serving endpoint supports the configured output mode. Anthropic
|
|
104
|
+
model listing preserves first-page pagination metadata; this client does not
|
|
105
|
+
fetch all pages automatically.
|
|
106
|
+
|
|
107
|
+
## Local or other compatible services
|
|
108
|
+
|
|
109
|
+
For a loopback server using Chat Completions, replace the provider configuration
|
|
110
|
+
in the complete example with:
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
const provider = createGenerativeProvider({
|
|
114
|
+
type: 'openai-compatible',
|
|
115
|
+
baseURL: 'http://127.0.0.1:8000/v1',
|
|
116
|
+
apiKey: null,
|
|
117
|
+
model: process.env.LOCAL_MODEL,
|
|
118
|
+
responseFormat: 'json_schema',
|
|
119
|
+
tokenParameter: 'max_tokens',
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Set `LOCAL_MODEL` in `.env` to your served model ID. `apiKey: null` explicitly
|
|
124
|
+
allows no authentication only for loopback compatible endpoints. A remote custom
|
|
125
|
+
endpoint requires HTTPS and an explicit `apiKey`, for example
|
|
126
|
+
`apiKey: process.env.MY_SERVICE_KEY`. See [credential boundaries](../reference/CONFIGURATION.md#custom-endpoint-credential-boundary).
|
|
127
|
+
|
|
128
|
+
Select `responseFormat: 'json_object'` or `'text'` only when appropriate for that
|
|
129
|
+
service. Validation remains strict, and the client does not silently retry with
|
|
130
|
+
a weaker format.
|
|
131
|
+
|
|
132
|
+
## Optional command runner
|
|
133
|
+
|
|
134
|
+
From an application with the package installed locally, create a request to edit:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm exec -- decision-bridge init ./my-decisions
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Edit `my-decisions/request.json` for your task. Keep `.env` in the application
|
|
141
|
+
root for the commands below, with the selected backend's key. Replace the
|
|
142
|
+
uppercase model placeholders with IDs available to your account/server:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
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 gpt-6-luna --env-file .env
|
|
147
|
+
npm exec -- decision-bridge evaluate ./my-decisions/request.json --provider cloudflare-decisions --model clef-flash --env-file .env
|
|
148
|
+
npm exec -- decision-bridge evaluate ./my-decisions/request.json --provider custom-generative --preset openrouter --model VENDOR/MODEL_ID --env-file .env
|
|
149
|
+
npm exec -- decision-bridge evaluate ./my-decisions/request.json --provider custom-generative --preset openai-compatible --model MODEL_ID --env-file .env
|
|
150
|
+
npm exec -- decision-bridge evaluate ./my-decisions/request.json --provider custom-generative --preset anthropic --model MODEL_ID --env-file .env
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
List models with `npm exec -- decision-bridge models --provider custom-generative --preset openrouter --env-file .env`.
|
|
154
|
+
For a loopback compatible server:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npm exec -- decision-bridge evaluate ./my-decisions/request.json --provider custom-generative --preset openai-compatible --base-url http://127.0.0.1:8000/v1 --no-auth --model LOCAL_MODEL_ID --token-parameter max_tokens --response-format json_schema
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
For a remote custom endpoint, explicitly select its credential variable with
|
|
161
|
+
`--api-key-env MY_SERVICE_KEY` alongside `--base-url`.
|
|
162
|
+
|
|
163
|
+
### Save a command-runner profile
|
|
164
|
+
|
|
165
|
+
Create `openrouter.config.json` in your application directory:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"schemaVersion": 1,
|
|
170
|
+
"provider": "custom-generative",
|
|
171
|
+
"preset": "openrouter",
|
|
172
|
+
"model": "VENDOR/MODEL_ID",
|
|
173
|
+
"apiKeyEnv": "OPENROUTER_API_KEY",
|
|
174
|
+
"responseFormat": "json_schema",
|
|
175
|
+
"timeoutMs": 30000
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
npm exec -- decision-bridge evaluate ./my-decisions/request.json --config openrouter.config.json --env-file .env
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Command-runner model precedence is `--model` > `request.model` > resolved
|
|
184
|
+
configuration > provider default. Omit `model` from reusable request files when
|
|
185
|
+
profiles should choose it. Provider, preset, and module changes reset lower-precedence
|
|
186
|
+
provider-specific settings; supply overrides for the new provider explicitly.
|
|
187
|
+
See [configuration](../reference/CONFIGURATION.md).
|
|
188
|
+
|
|
189
|
+
## Boundaries
|
|
190
|
+
|
|
191
|
+
The opt-in custom generative compatible preset uses Chat Completions, not the OpenAI Responses API.
|
|
192
|
+
Anthropic uses one forced output-schema tool, not an agent loop. A different
|
|
193
|
+
protocol requires the [custom-provider contract](CUSTOM_PROVIDERS.md).
|
|
194
|
+
Jev, OpenAI Decisions, and Cloudflare Clef each have a dedicated native provider;
|
|
195
|
+
use those provider types for their decision APIs. Native Decisions providers
|
|
196
|
+
reject generative output settings such as `responseFormat` and `maxTokens`.
|
|
197
|
+
|
|
198
|
+
All shipped evaluations return probability-based decisions. Jev, OpenAI Decisions,
|
|
199
|
+
and Cloudflare probabilities are marked `native`; generative estimates are `elicited`, not logprobs or
|
|
200
|
+
a calibration guarantee. There is no discrete-only mode in this release. The
|
|
201
|
+
bridge standardizes the software interface, not model accuracy or statistical
|
|
202
|
+
semantics. See [provider reference](../reference/PROVIDERS.md).
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
## The command cannot find a key
|
|
4
|
+
|
|
5
|
+
General `node bin/decision.mjs ...` calls need exported environment variables or an
|
|
6
|
+
explicit `--env-file .env`. Only the documented convenience npm scripts
|
|
7
|
+
preload `.env`. The file must name the selected provider's credential variable.
|
|
8
|
+
Do not pass a TypeSafe key to OpenRouter or vice versa. Existing shell environment
|
|
9
|
+
values win over a file; an old exported value can therefore mask a newly edited file.
|
|
10
|
+
|
|
11
|
+
## My model or base URL seems ignored
|
|
12
|
+
|
|
13
|
+
Unknown options are rejected, but model precedence still matters:
|
|
14
|
+
`--model > request.model > resolved config/environment model > provider default`.
|
|
15
|
+
Provider changes discard
|
|
16
|
+
incompatible fields inherited from a different JSON profile. Use a complete
|
|
17
|
+
explicit config. A compatible base includes its version prefix, not the final
|
|
18
|
+
`/chat/completions` path. Jev and the custom Anthropic preset require origins
|
|
19
|
+
without a path. See [configuration](../reference/CONFIGURATION.md#precedence).
|
|
20
|
+
|
|
21
|
+
## Structured output is rejected
|
|
22
|
+
|
|
23
|
+
Check that the selected endpoint/model supports the requested `json_schema` mode.
|
|
24
|
+
Choose `json_object` or `text` explicitly when appropriate; local validation remains
|
|
25
|
+
strict. Some servers require `--token-parameter max_tokens`. A valid schema does
|
|
26
|
+
not guarantee probabilities sum to one. `INVALID_RESPONSE` must not be treated as
|
|
27
|
+
an approved decision. Large schemas also consume context/output budget.
|
|
28
|
+
|
|
29
|
+
## The model returns JSON but confidence is absent
|
|
30
|
+
|
|
31
|
+
This is expected for generative backends. They produce elicited probabilities
|
|
32
|
+
and do not report the native confidence carried by Jev, OpenAI Decisions, or
|
|
33
|
+
Cloudflare choice/score answers. Read `meta.probabilitySource`. A null token
|
|
34
|
+
count means the server did not report it. Neither absence should be silently
|
|
35
|
+
converted to a number.
|
|
36
|
+
|
|
37
|
+
## Cloudflare needs an account ID as well as a token
|
|
38
|
+
|
|
39
|
+
REST inference targets an account-specific URL. Supply `CLOUDFLARE_ACCOUNT_ID`
|
|
40
|
+
and `CLOUDFLARE_API_TOKEN`, or explicit SDK credentials, for that account.
|
|
41
|
+
Standard REST inference needs no Worker or Gateway. Binding calls instead require
|
|
42
|
+
an injected `env.AI`; Gateway routing requires explicit Gateway configuration.
|
|
43
|
+
See the [Cloudflare guide](CLOUDFLARE_DECISIONS.md#supported-inference-paths).
|
|
44
|
+
|
|
45
|
+
## Cloudflare REST busy mode is unavailable
|
|
46
|
+
|
|
47
|
+
The bridge rejects enabled Clef REST busy mode locally with
|
|
48
|
+
`UNSUPPORTED_CAPABILITY` before inference. Direct hosted requests were observed
|
|
49
|
+
to reject the execution field with HTTP 422/code `5012`. Leave `rejectIfBusy`
|
|
50
|
+
disabled for REST, or use the Workers AI binding for the enabled setting.
|
|
51
|
+
This differs from a capacity rejection (`3040`) or an allocation limit (`3036`);
|
|
52
|
+
increasing client retries does not fix unsupported
|
|
53
|
+
execution options. See [capacity and Gateway settings](CLOUDFLARE_DECISIONS.md#capacity-rejection-and-ai-gateway).
|
|
54
|
+
|
|
55
|
+
## Evaluation succeeds but an extension failed
|
|
56
|
+
|
|
57
|
+
Inspect `meta.observerFailures`. Observation occurs after inference; a failed log
|
|
58
|
+
sink must not cause another model call. Preparation failures instead throw
|
|
59
|
+
`EXTENSION_ERROR` before continuing to inference. Observe the separate provider,
|
|
60
|
+
bridge, and observer deadlines documented in the SDK reference.
|
|
61
|
+
|
|
62
|
+
## Batch output is incomplete
|
|
63
|
+
|
|
64
|
+
Per-record errors are explicit JSONL records and do not stop the CLI. A global
|
|
65
|
+
input, UTF-8, limit, interruption, or filesystem failure can stop the batch. Atomic
|
|
66
|
+
file output is not finalized in that case; stdout can contain a completed prefix.
|
|
67
|
+
Some requests may already have been sent/billed. Retrying an entire prefix can
|
|
68
|
+
repeat work, so retain completed records externally when designing resumability.
|
|
69
|
+
|
|
70
|
+
## Type checks fail because tsc is missing
|
|
71
|
+
|
|
72
|
+
Runtime and tests have no TypeScript dependency. Development type checks require
|
|
73
|
+
TypeScript 5.8.3, the verified compiler. Install it as a developer tool using the
|
|
74
|
+
instructions in [Testing](../development/TESTING.md). Do not confuse an absent
|
|
75
|
+
development compiler with a broken runtime package.
|
|
76
|
+
|
|
77
|
+
## Filing a useful issue
|
|
78
|
+
|
|
79
|
+
Include bridge version, Node version, OS, command with secrets removed, provider,
|
|
80
|
+
requested model, safe error JSON, and a minimal nonsensitive reproduction. Never
|
|
81
|
+
include `.env`, keys, private prompts, or raw confidential documents. Security
|
|
82
|
+
reports should be private; see the root security policy.
|