@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.
Files changed (112) hide show
  1. package/.env.example +10 -0
  2. package/CHANGELOG.md +75 -0
  3. package/CONTRIBUTING.md +57 -0
  4. package/LICENSE +21 -0
  5. package/README.md +307 -2
  6. package/SECURITY.md +88 -0
  7. package/bin/decision.mjs +15 -0
  8. package/docs/README.md +58 -0
  9. package/docs/development/ARCHITECTURE.md +118 -0
  10. package/docs/development/DESIGN.md +66 -0
  11. package/docs/development/README.md +12 -0
  12. package/docs/development/RELEASING.md +100 -0
  13. package/docs/development/REPOSITORY_LAYOUT.md +33 -0
  14. package/docs/development/TESTING.md +131 -0
  15. package/docs/guides/BROWSER.md +43 -0
  16. package/docs/guides/CLOUDFLARE_DECISIONS.md +374 -0
  17. package/docs/guides/CUSTOMIZATION.md +141 -0
  18. package/docs/guides/CUSTOM_DECISION.md +66 -0
  19. package/docs/guides/CUSTOM_GENERATIVE.md +164 -0
  20. package/docs/guides/CUSTOM_PROVIDERS.md +81 -0
  21. package/docs/guides/GETTING_STARTED.md +252 -0
  22. package/docs/guides/OPENAI_DECISIONS.md +210 -0
  23. package/docs/guides/PLATFORMS.md +107 -0
  24. package/docs/guides/SWITCHING_MODELS.md +202 -0
  25. package/docs/guides/TROUBLESHOOTING.md +82 -0
  26. package/docs/reference/CLI.md +202 -0
  27. package/docs/reference/CONFIGURATION.md +168 -0
  28. package/docs/reference/ERRORS.md +82 -0
  29. package/docs/reference/PROVIDERS.md +113 -0
  30. package/docs/reference/SDK.md +388 -0
  31. package/docs/reference/SOURCES.md +51 -0
  32. package/examples/basic.mjs +17 -0
  33. package/examples/batch.mjs +13 -0
  34. package/examples/cloudflare-decisions.mjs +22 -0
  35. package/examples/cloudflare-omni.mjs +32 -0
  36. package/examples/cloudflare-worker.mjs +21 -0
  37. package/examples/cloudflare.native.json +28 -0
  38. package/examples/configs/anthropic.json +9 -0
  39. package/examples/configs/cloudflare-decisions.json +8 -0
  40. package/examples/configs/custom-decision.json +9 -0
  41. package/examples/configs/custom-generative.json +14 -0
  42. package/examples/configs/jev.json +7 -0
  43. package/examples/configs/local.json +12 -0
  44. package/examples/configs/openai-decisions.json +7 -0
  45. package/examples/configs/openai.json +10 -0
  46. package/examples/configs/openrouter.json +10 -0
  47. package/examples/custom/decision-provider.mjs +15 -0
  48. package/examples/custom/generative-provider.mjs +13 -0
  49. package/examples/custom/openrouter.mjs +13 -0
  50. package/examples/customize.mjs +23 -0
  51. package/examples/decisions.native.json +19 -0
  52. package/examples/extended.mjs +43 -0
  53. package/examples/openai-decisions.mjs +20 -0
  54. package/examples/request.json +31 -0
  55. package/examples/requests.jsonl +2 -0
  56. package/package.json +87 -4
  57. package/schemas/decision.config.schema.json +553 -0
  58. package/src/async.mjs +63 -0
  59. package/src/batch.mjs +38 -0
  60. package/src/bridge.mjs +184 -0
  61. package/src/capabilities.mjs +95 -0
  62. package/src/cli/config-validation.mjs +89 -0
  63. package/src/cli/config.mjs +84 -0
  64. package/src/cli/decisions.mjs +19 -0
  65. package/src/cli/initialize.mjs +79 -0
  66. package/src/cli/io.mjs +171 -0
  67. package/src/cli/main.mjs +280 -0
  68. package/src/cli/ordered.mjs +43 -0
  69. package/src/cli/provider-loader.mjs +58 -0
  70. package/src/cloudflare-decisions.d.mts +78 -0
  71. package/src/custom/decision.d.mts +5 -0
  72. package/src/custom/decision.mjs +6 -0
  73. package/src/custom/definition.mjs +19 -0
  74. package/src/custom/generative/anthropic.mjs +75 -0
  75. package/src/custom/generative/index.d.mts +42 -0
  76. package/src/custom/generative/index.mjs +29 -0
  77. package/src/custom/generative/openai-compatible.mjs +101 -0
  78. package/src/custom/generative/schema.mjs +95 -0
  79. package/src/custom/generative/shared.mjs +39 -0
  80. package/src/decisions.d.mts +45 -0
  81. package/src/embedded.mjs +24 -0
  82. package/src/errors.mjs +49 -0
  83. package/src/index.d.mts +229 -0
  84. package/src/index.mjs +7 -0
  85. package/src/input.mjs +37 -0
  86. package/src/options.mjs +15 -0
  87. package/src/policy.mjs +44 -0
  88. package/src/probabilities.mjs +27 -0
  89. package/src/provider-contract.mjs +42 -0
  90. package/src/providers/cloudflare-audio.mjs +83 -0
  91. package/src/providers/cloudflare-codec.mjs +39 -0
  92. package/src/providers/cloudflare-decisions.mjs +81 -0
  93. package/src/providers/cloudflare-images.mjs +95 -0
  94. package/src/providers/cloudflare-media.mjs +36 -0
  95. package/src/providers/cloudflare-preflight.mjs +92 -0
  96. package/src/providers/cloudflare-profile.mjs +102 -0
  97. package/src/providers/cloudflare-transport.mjs +63 -0
  98. package/src/providers/cloudflare-validation.mjs +48 -0
  99. package/src/providers/cloudflare-video.mjs +84 -0
  100. package/src/providers/decisions-codec.mjs +61 -0
  101. package/src/providers/decisions-preflight.mjs +20 -0
  102. package/src/providers/decisions-validation.mjs +150 -0
  103. package/src/providers/index.mjs +26 -0
  104. package/src/providers/jev.mjs +51 -0
  105. package/src/providers/media-binary.mjs +51 -0
  106. package/src/providers/openai-decisions.mjs +51 -0
  107. package/src/questions.mjs +15 -0
  108. package/src/response-transport.mjs +176 -0
  109. package/src/runtime-env.mjs +4 -0
  110. package/src/transport.mjs +51 -0
  111. package/src/validation.mjs +245 -0
  112. 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.