@fayegram-ai/decision-bridge 0.0.0-stage → 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/.env.example +10 -0
- package/CHANGELOG.md +79 -0
- package/CONTRIBUTING.md +57 -0
- package/LICENSE +21 -0
- package/README.md +313 -2
- package/SECURITY.md +88 -0
- package/bin/decision.mjs +15 -0
- package/docs/README.md +58 -0
- package/docs/development/ARCHITECTURE.md +139 -0
- package/docs/development/DESIGN.md +77 -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 +153 -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 +83 -0
- package/docs/guides/GETTING_STARTED.md +252 -0
- package/docs/guides/OPENAI_DECISIONS.md +252 -0
- package/docs/guides/PLATFORMS.md +107 -0
- package/docs/guides/SWITCHING_MODELS.md +208 -0
- package/docs/guides/TROUBLESHOOTING.md +96 -0
- package/docs/reference/CLI.md +213 -0
- package/docs/reference/CONFIGURATION.md +170 -0
- package/docs/reference/ERRORS.md +85 -0
- package/docs/reference/PROVIDERS.md +116 -0
- package/docs/reference/SDK.md +394 -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 +92 -0
- package/src/cli/config-validation.mjs +89 -0
- package/src/cli/config.mjs +85 -0
- package/src/cli/decisions.mjs +21 -0
- package/src/cli/initialize.mjs +85 -0
- package/src/cli/io.mjs +171 -0
- package/src/cli/main.mjs +281 -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 +50 -0
- package/src/embedded.mjs +24 -0
- package/src/errors.mjs +49 -0
- package/src/index.d.mts +232 -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 +31 -0
- package/src/providers/decisions-validation.mjs +151 -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 +58 -0
- package/src/providers/openai-profile.mjs +24 -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,394 @@
|
|
|
1
|
+
# JavaScript SDK reference
|
|
2
|
+
|
|
3
|
+
Import from `@fayegram-ai/decision-bridge` in your application after package installation.
|
|
4
|
+
See [getting started](../guides/GETTING_STARTED.md) for a complete JavaScript call,
|
|
5
|
+
TypeScript setup, and CommonJS dynamic import. The source-tree entry point is
|
|
6
|
+
`src/index.mjs`. The package exposes ESM. Public
|
|
7
|
+
TypeScript declarations are in `src/index.d.mts`; runtime JavaScript is authoritative
|
|
8
|
+
and checked independently of those declarations.
|
|
9
|
+
|
|
10
|
+
## Create a bridge
|
|
11
|
+
|
|
12
|
+
```javascript
|
|
13
|
+
import { createBridge, createProvider } from '@fayegram-ai/decision-bridge';
|
|
14
|
+
|
|
15
|
+
const provider = createProvider({
|
|
16
|
+
type: 'jev', model: 'jev-latest', timeoutMs: 30000, maxRetries: 2,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const bridge = createBridge({
|
|
20
|
+
provider,
|
|
21
|
+
timeoutMs: 60000,
|
|
22
|
+
observerTimeoutMs: 1000,
|
|
23
|
+
extensions: [],
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Select exactly one non-undefined `provider` (a `Provider` with an explicit kind) or `jev`
|
|
28
|
+
(native Jev options). The explicit shortcut `createBridge({ jev: {} })` reads
|
|
29
|
+
Jev's credentials and model defaults. Missing selection, null selectors, and
|
|
30
|
+
simultaneous selections are configuration errors. The exported `BridgeOptions`
|
|
31
|
+
type requires selection, and `createBridge` returns a `Bridge`.
|
|
32
|
+
Selection uses own configuration properties: inherited `provider`, `jev`, or
|
|
33
|
+
factory `type` properties do not select a provider, and their getters are not read.
|
|
34
|
+
The bridge's `provider`, `providerKind`, and `defaultModel` properties are read-only.
|
|
35
|
+
`providerKind` is `decision` or `generative`, selected from the provider contract.
|
|
36
|
+
Unknown options are errors, not ignored settings. Provider credentials are resolved
|
|
37
|
+
at construction time; create a new provider after rotating credentials.
|
|
38
|
+
|
|
39
|
+
### Provider transport options
|
|
40
|
+
|
|
41
|
+
Native REST providers and maintained custom generative presets accept the public
|
|
42
|
+
`TransportOptions` fields below through their constructors or the appropriate
|
|
43
|
+
provider factory. Model, credentials, endpoint selection, and generation settings
|
|
44
|
+
remain provider-specific; see [configuration](CONFIGURATION.md) and the provider
|
|
45
|
+
guides. These are SDK options, not per-evaluation bridge options.
|
|
46
|
+
|
|
47
|
+
| Option | Default and behavior |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `timeoutMs` | 30,000 ms; positive integer through 2,147,483,647; one transport budget for dispatch, retries, backoff, and response reading |
|
|
50
|
+
| `maxRetries` | Additional eligible attempts, 0–10; Jev and generative presets default to 2, native OpenAI and Cloudflare to 0 |
|
|
51
|
+
| `maxRequestBytes` | Complete serialized request ceiling; normally 8 MiB, configurable from 1 byte through 64 MiB; Cloudflare applies its [model-specific budgets](../guides/CLOUDFLARE_DECISIONS.md#embedded-images-and-limits) |
|
|
52
|
+
| `maxResponseBytes` | 8 MiB; configurable from 1 byte through 64 MiB; applies while reading the response body |
|
|
53
|
+
| `fetchImpl` | Optional `(url: string, init?: RequestInit) => Promise<Response>`; defaults to the runtime's `fetch` |
|
|
54
|
+
|
|
55
|
+
Use `fetchImpl` to wrap or inject a fetch implementation in an application or
|
|
56
|
+
local protocol test. The SDK supplies the destination URL, method, headers, body,
|
|
57
|
+
abort signal, and `redirect: 'error'`. A wrapper must preserve those settings and
|
|
58
|
+
return a standard byte-stream response; it owns the behavior of any replacement
|
|
59
|
+
networking. The CLI's JSON configuration cannot contain functions, so this option
|
|
60
|
+
is SDK-only. Cloudflare binding transport accepts the timeout, retry, and byte
|
|
61
|
+
controls, but rejects `fetchImpl` and REST credential/endpoint options.
|
|
62
|
+
|
|
63
|
+
The bridge separately defaults to a 60,000 ms evaluation budget and a 1,000 ms
|
|
64
|
+
timeout per observer. Call-level `timeoutMs` changes the bridge budget, while
|
|
65
|
+
provider constructor `timeoutMs` changes the transport budget. The
|
|
66
|
+
[cancellation and deadlines](#cancellation-and-deadlines) section explains how
|
|
67
|
+
they interact; [errors](ERRORS.md#retry-policy) lists retryable statuses.
|
|
68
|
+
|
|
69
|
+
### `bridge.evaluate(request, options?)`
|
|
70
|
+
|
|
71
|
+
`request` has exactly one of `state` or ordered `input`, `questions`, and optional
|
|
72
|
+
`model` and `safetyIdentifier`. New forms require declared provider capabilities;
|
|
73
|
+
see [OpenAI Decisions](../guides/OPENAI_DECISIONS.md) and
|
|
74
|
+
[Cloudflare Clef](../guides/CLOUDFLARE_DECISIONS.md). The request model
|
|
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.
|
|
79
|
+
`options` accepts `signal: AbortSignal` and `timeoutMs`, overriding the bridge's
|
|
80
|
+
60-second evaluation budget for this call. It does not mutate the provider's
|
|
81
|
+
independent transport timeout, including Workers AI binding calls.
|
|
82
|
+
|
|
83
|
+
Ordered parts are `{ type: 'text', text }`, `{ type: 'image', dataUrl, detail? }`,
|
|
84
|
+
`{ type: 'audio', dataUrl }`, or `{ type: 'video', dataUrl }`. Audio/video are
|
|
85
|
+
supported by Cloudflare Clef-omni and explicitly capable custom providers; other
|
|
86
|
+
built-ins reject them before dispatch. Cloudflare requires images → audio →
|
|
87
|
+
videos → text. Media use embedded data URLs, with format and size checks described
|
|
88
|
+
in the [Cloudflare guide](../guides/CLOUDFLARE_DECISIONS.md#omni-audio-and-video).
|
|
89
|
+
|
|
90
|
+
The bridge snapshots and freezes its input, runs sequential state or input preparation,
|
|
91
|
+
invokes the provider once, validates the result, then observes an immutable result.
|
|
92
|
+
Built-in transports can retry eligible response statuses within that invocation
|
|
93
|
+
when client retries are configured.
|
|
94
|
+
|
|
95
|
+
### `bridge.getCapabilities(model?)` and `bridge.validate(request)`
|
|
96
|
+
|
|
97
|
+
Capability lookup is synchronous and reports implemented features and limits.
|
|
98
|
+
`validate` applies the default model selection, snapshots/freezes the request, and checks capabilities
|
|
99
|
+
and preparation-extension compatibility without inference. Unsupported features
|
|
100
|
+
fail with `UNSUPPORTED_CAPABILITY` before dispatch. Native Decisions has no model
|
|
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.
|
|
104
|
+
|
|
105
|
+
Providers can implement optional `validateRequest(resolvedRequest)` for additional
|
|
106
|
+
static checks. This synchronous hook receives a frozen request, must return no
|
|
107
|
+
value, and rejects by throwing; it must not perform inference or other side
|
|
108
|
+
effects. OpenAI Decisions and Cloudflare Clef use it to check native fields and UTF-8 request
|
|
109
|
+
bytes against `maxRequestBytes`. The bridge repeats validation after preparation.
|
|
110
|
+
|
|
111
|
+
### `bridge.listModels(options?)`
|
|
112
|
+
|
|
113
|
+
Calls the provider's listing operation. Returns `{ models: [{ name, ... }], ... }`.
|
|
114
|
+
Pagination markers are preserved; this is not a promise of an exhaustive catalog.
|
|
115
|
+
OpenAI Decisions, Cloudflare Clef, and custom providers without listing support
|
|
116
|
+
throw `UNSUPPORTED_OPERATION`.
|
|
117
|
+
|
|
118
|
+
## Request data
|
|
119
|
+
|
|
120
|
+
`state` is a string, plain JSON object, or array. It may be empty, but not null,
|
|
121
|
+
a top-level number, or a top-level boolean. Values nested within objects/arrays can
|
|
122
|
+
be any JSON values. JSON snapshots reject `undefined`, cycles, non-finite numbers,
|
|
123
|
+
functions, dates, class instances, accessors, symbols, sparse arrays, and extra or
|
|
124
|
+
non-enumerable array/object properties. Limits: nesting depth 100, 200,000 nodes.
|
|
125
|
+
|
|
126
|
+
`questions` is a nonempty object keyed by application-owned identifiers. Every
|
|
127
|
+
question requires non-null `instructions`: nonempty text, a JSON object, or an
|
|
128
|
+
array. Descriptions can be text or structured JSON. Only choice descriptions permit
|
|
129
|
+
raw null. Question IDs and legacy choice-map keys must be nonempty; typed string
|
|
130
|
+
choice values may be empty. Request and question unknown fields are rejected.
|
|
131
|
+
|
|
132
|
+
### Question builders
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
const q1 = choice('Which queue should receive this?', {
|
|
136
|
+
billing: 'Invoices or payment problems.',
|
|
137
|
+
engineering: 'Technical defects.',
|
|
138
|
+
unknown: 'Insufficient evidence.',
|
|
139
|
+
});
|
|
140
|
+
const q2 = noul('Does the evidence show a failed test?', {
|
|
141
|
+
true: 'At least one executed test failed.',
|
|
142
|
+
false: 'No executed test is shown to have failed.',
|
|
143
|
+
});
|
|
144
|
+
const q3 = score('How far did processing progress?', [
|
|
145
|
+
'Not started.', 'Started but incomplete.', 'Completed.',
|
|
146
|
+
]);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Builders return immutable question objects. `choice(instructions, criteria)` accepts
|
|
150
|
+
a label map containing 1 to 255 options. `noul(instructions, criteria?)` accepts an
|
|
151
|
+
optional criteria object,
|
|
152
|
+
with only `true` and `false` keys. `score(instructions, criteria)` accepts **2–10**
|
|
153
|
+
ordered levels; numeric level indices are zero-based. There is no boolean alias:
|
|
154
|
+
`noul` means a probability, not a thresholded truth value.
|
|
155
|
+
|
|
156
|
+
`choice(instructions, options)` accepts ordered objects with unique string or
|
|
157
|
+
boolean `value` fields and optional text or structured JSON descriptions. Boolean `true` and string
|
|
158
|
+
`"true"` remain distinct. `scoreLevels(instructions, levels)` accepts at least two
|
|
159
|
+
ordered label/description objects. Provider preflight applies service limits:
|
|
160
|
+
OpenAI Decisions and Cloudflare require 2–255 choices; Cloudflare limits scores
|
|
161
|
+
to 2–10 levels, while OpenAI labelled scores have no additional client count cap.
|
|
162
|
+
|
|
163
|
+
## Returned data
|
|
164
|
+
|
|
165
|
+
```text
|
|
166
|
+
EvaluationResult
|
|
167
|
+
model Reported model identity, or explicitly marked fallback
|
|
168
|
+
answers.<question-id> Typed answer, matched to that input question
|
|
169
|
+
usage Token counts; unreported generic counts are null
|
|
170
|
+
meta Provenance, version, timings, diagnostics
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Legacy choice:** `{ type: 'choice', choice, probabilities, confidence? }`.
|
|
174
|
+
The probability keys must exactly match the options, all values must be in [0,1],
|
|
175
|
+
and the sum must be within 0.001 of 1, except for the provider-selected rounding
|
|
176
|
+
allowance described below. The selected option must be maximal within
|
|
177
|
+
rounding tolerance. Jev requires reported choice/score confidence; the other
|
|
178
|
+
native providers validate and preserve their service-specific confidence.
|
|
179
|
+
Generative providers derive the selected option from
|
|
180
|
+
the distribution and omit confidence. Generative ties choose the first label in
|
|
181
|
+
JavaScript property enumeration order; numeric-looking keys
|
|
182
|
+
follow JavaScript's numeric property ordering.
|
|
183
|
+
Native selections are retained after validation.
|
|
184
|
+
|
|
185
|
+
Typed choice questions instead return a typed string/boolean selection and an
|
|
186
|
+
array of `{ value, probability }` entries. See the [Decisions guide](../guides/OPENAI_DECISIONS.md).
|
|
187
|
+
|
|
188
|
+
Cloudflare selects four-decimal probability validation. Distributions aligned to
|
|
189
|
+
that precision allow `max(0.001, optionCount * 0.00005)`, plus a small floating-point
|
|
190
|
+
allowance. Other Cloudflare distributions and existing providers retain the 0.001
|
|
191
|
+
baseline. The bridge selects this policy from the provider contract, never from
|
|
192
|
+
response metadata, and preserves all returned numbers.
|
|
193
|
+
|
|
194
|
+
**Noul:** `{ type: 'noul', noul }`, with a number in [0,1]. The library never
|
|
195
|
+
thresholds this into true/false without application code.
|
|
196
|
+
|
|
197
|
+
**Score:** `{ type: 'score', score, probabilities, legend, confidence? }`.
|
|
198
|
+
For N levels, keys are `'0'` through `String(N-1)`. The score is the weighted mean
|
|
199
|
+
of these indices; validation tolerance is `0.02 * N` (accommodating 2-decimal rounded probability distributions from live services). `legend` must match the input
|
|
200
|
+
rubric exactly. Generative providers calculate score and legend locally. Native
|
|
201
|
+
values are validated and preserved, not normalized or replaced.
|
|
202
|
+
|
|
203
|
+
**Usage:** `input_tokens` and `output_tokens` are nonnegative integers or null.
|
|
204
|
+
Jev, OpenAI Decisions, and Cloudflare require reported integer counts. OpenAI and
|
|
205
|
+
Anthropic cache creation/read counts are preserved separately when provided;
|
|
206
|
+
Cloudflare does not fabricate cache or total-token fields. No total price, inferred cache saving, or
|
|
207
|
+
estimated token count is fabricated.
|
|
208
|
+
|
|
209
|
+
**Metadata:**
|
|
210
|
+
|
|
211
|
+
| Field | Meaning |
|
|
212
|
+
| --- | --- |
|
|
213
|
+
| `provider` | Bridge backend name; native Jev reports `typesafe` |
|
|
214
|
+
| `providerKind` | `decision` or `generative`; authoritative provider classification, never response-selected |
|
|
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 |
|
|
217
|
+
| `probabilitySource` | `native`, `elicited`, or custom/`unspecified` provenance |
|
|
218
|
+
| `confidenceSource` | `native` or `unavailable` for built-in providers |
|
|
219
|
+
| `modelSource` | `reported` or `requested` fallback for built-in providers |
|
|
220
|
+
| `bridgeVersion`, `schemaVersion` | Package version and result contract version |
|
|
221
|
+
| `attempts`, `requestId` | Transport diagnostics when supplied |
|
|
222
|
+
| `gatewayLogId` | Cloudflare Gateway response identifier when reported |
|
|
223
|
+
| `openaiDecisions`, `cloudflareDecisions` | Provider-specific reported usage and native answer/confidence metadata |
|
|
224
|
+
| `durationMs` | Provider transport duration, including retries and body reading |
|
|
225
|
+
| `totalDurationMs` | Bridge duration through observation |
|
|
226
|
+
| `observerDurationMs` | Observation phase duration; initially zero inside observers |
|
|
227
|
+
| `observerFailures` | Names of failed/timed-out observers |
|
|
228
|
+
|
|
229
|
+
Metadata can have additional JSON fields. Application logic should not assume
|
|
230
|
+
unavailable metadata exists. Result objects and nested data returned by the bridge
|
|
231
|
+
are immutable. Standalone validation functions return snapshots, not frozen values.
|
|
232
|
+
|
|
233
|
+
`schemaVersion` remains 1 for legacy requests and 2 for ordered images/text or
|
|
234
|
+
richer question forms. Audio/video requests use 3. This result version is separate
|
|
235
|
+
from the CLI configuration schema, which remains 1.
|
|
236
|
+
|
|
237
|
+
## Extensions
|
|
238
|
+
|
|
239
|
+
An extension has a unique printable `name` (maximum 100 characters), optional
|
|
240
|
+
`prepareState(state, context)`, `prepareInput(input, context)`, and
|
|
241
|
+
`onResult(result, context)`. Preparation extensions must support the request's
|
|
242
|
+
input form; incompatible hooks fail explicitly before dispatch.
|
|
243
|
+
|
|
244
|
+
Preparation returns replacement state or input, or `undefined` to leave it unchanged.
|
|
245
|
+
Preparation exceptions fail before inference with `EXTENSION_ERROR`; transformed
|
|
246
|
+
content is revalidated, including capabilities and provider preflight. Preparation
|
|
247
|
+
cannot rewrite question definitions or switch the model.
|
|
248
|
+
Construct the request directly for those changes.
|
|
249
|
+
|
|
250
|
+
Observers run after successful inference; their failures are collected in
|
|
251
|
+
`meta.observerFailures`. They do not change answers, fail the successful request,
|
|
252
|
+
or trigger another billed call. Each observer gets its own timeout, default 1,000
|
|
253
|
+
milliseconds. Results shown to observers precede final timing/failure aggregation.
|
|
254
|
+
|
|
255
|
+
`context` contains `provider`, `model`, frozen `questionIds`, and an `AbortSignal`.
|
|
256
|
+
Custom callbacks should honor the signal. The bridge can stop waiting for ignored
|
|
257
|
+
signals, but cannot terminate arbitrary user code or its external side effects.
|
|
258
|
+
See [customization](../guides/CUSTOMIZATION.md) for runnable examples.
|
|
259
|
+
|
|
260
|
+
## Cancellation and deadlines
|
|
261
|
+
|
|
262
|
+
```javascript
|
|
263
|
+
const controller = new AbortController();
|
|
264
|
+
const resultPromise = bridge.evaluate(request, {
|
|
265
|
+
signal: controller.signal,
|
|
266
|
+
timeoutMs: 15000,
|
|
267
|
+
});
|
|
268
|
+
// controller.abort() cancels the in-flight request when needed.
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The transport deadline covers dispatch, attempts, backoff, and body reading for
|
|
272
|
+
REST and Workers AI binding calls. A bridge deadline
|
|
273
|
+
also covers preparation and the provider. The first applicable deadline wins.
|
|
274
|
+
Observer time is additional and bounded separately. Aborting after a successful
|
|
275
|
+
inference while observing preserves the successful result and records observer
|
|
276
|
+
failure; it does not pretend the billed evaluation failed.
|
|
277
|
+
|
|
278
|
+
Elapsed time is checked before dispatch and before accepting completion, including
|
|
279
|
+
when synchronous work delays timer callbacks. Nested transport calls inherit the
|
|
280
|
+
remaining bridge deadline. Synchronous user code cannot be interrupted; an
|
|
281
|
+
overrun is detected when control returns, without undoing completed side effects.
|
|
282
|
+
|
|
283
|
+
## SDK batch evaluation
|
|
284
|
+
|
|
285
|
+
```javascript
|
|
286
|
+
const records = await evaluateBatch(bridge, requests, {
|
|
287
|
+
concurrency: 4,
|
|
288
|
+
stopOnError: false,
|
|
289
|
+
signal: controller.signal,
|
|
290
|
+
});
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
This array API validates all raw requests, including provider preflight, before
|
|
294
|
+
dispatch. Preparation runs during each evaluation, so errors introduced by hooks
|
|
295
|
+
remain per-request failures. Maximum array length is
|
|
296
|
+
10,000; concurrency is 1–64. Records retain input order and are either
|
|
297
|
+
`{ index, ok: true, result }` or `{ index, ok: false, error }`.
|
|
298
|
+
With `stopOnError`, already-started work completes and new work is marked
|
|
299
|
+
`BATCH_SKIPPED`. Cancellation marks undispatched records `ABORTED`. There is no
|
|
300
|
+
automatic model fallback. The CLI has a separate streaming JSONL implementation.
|
|
301
|
+
|
|
302
|
+
## Additional exports
|
|
303
|
+
|
|
304
|
+
- `createProvider(config)`: factory requiring an explicit `type` equal to `jev`,
|
|
305
|
+
`openai-decisions`, or `cloudflare-decisions`.
|
|
306
|
+
- `JevProvider`, `OpenAIDecisionsProvider`, `CloudflareDecisionsProvider`: native provider classes.
|
|
307
|
+
- `scoreLevels(instructions, levels)` and the array overload of `choice`:
|
|
308
|
+
labelled rubrics and typed string/boolean options, with provider capability checks.
|
|
309
|
+
- `OpenAIDecisionsProvider.createDecision(request, options?)`: dedicated native
|
|
310
|
+
access returning `{ decision, meta }`; see the [Decisions guide](../guides/OPENAI_DECISIONS.md).
|
|
311
|
+
- `CloudflareDecisionsProvider.createDecision(request, options?)`: dedicated native
|
|
312
|
+
Clef access returning `{ decision, meta }`; see the [Cloudflare guide](../guides/CLOUDFLARE_DECISIONS.md).
|
|
313
|
+
- `validateRequest(input, defaultModel?)`: snapshot and validate. Requires
|
|
314
|
+
an own `input.model` property or an explicitly supplied default; inherited
|
|
315
|
+
models are ignored, and it never selects a Jev model.
|
|
316
|
+
Question builders and schema generation validate without selecting an inference model.
|
|
317
|
+
- `validateResult(input, questions, { native = false, probabilityDecimalPlaces })`: snapshot supplied
|
|
318
|
+
questions and validate the canonical result; native mode requires confidence
|
|
319
|
+
and reported counts.
|
|
320
|
+
- `decideChoice(answer, { minProbability, minMargin = 0, abstainOptions = [], probabilityDecimalPlaces })`:
|
|
321
|
+
returns a policy result, never executes an action. `minProbability` is required.
|
|
322
|
+
- `DecisionError`, `publicError(error)`: public structured failure handling.
|
|
323
|
+
|
|
324
|
+
`decideChoice` returns `status: 'selected' | 'review'`, the model's choice, selected
|
|
325
|
+
probability, margin to the runner-up, and a reason. Thresholds are your policy;
|
|
326
|
+
no default claims of safety or empirical calibration are implied.
|
|
327
|
+
Its optional `probabilityDecimalPlaces` uses the same sum validation as
|
|
328
|
+
`validateResult`. Supply the precision from the provider's contract when using
|
|
329
|
+
rounded distributions, such as Cloudflare's four-decimal results. Omission keeps
|
|
330
|
+
the existing 0.001 tolerance; the helper never infers precision from an answer.
|
|
331
|
+
|
|
332
|
+
## Custom entry points
|
|
333
|
+
|
|
334
|
+
The root exports common `Provider`, `ProviderKind`, `DecisionProvider`, request,
|
|
335
|
+
result, and transport types. Every provider requires `kind`. Generative classes
|
|
336
|
+
and types use the explicit `custom/generative` entry point; the root provider
|
|
337
|
+
factory selects native decision providers.
|
|
338
|
+
|
|
339
|
+
| Entry point | Runtime exports | Types |
|
|
340
|
+
| --- | --- | --- |
|
|
341
|
+
| `custom/decision` | `defineDecisionProvider` | `DecisionProviderDefinition`; `DecisionProvider` |
|
|
342
|
+
| `custom/generative` | `defineGenerativeProvider`, `createGenerativeProvider`, `OpenAICompatibleProvider`, `OpenRouterProvider`, `AnthropicProvider` | `GenerativeProvider`, definition/config, and preset options |
|
|
343
|
+
| `custom/generative` | `buildResponseSchema`, `buildPrompt`, `parseGeneratedJson`, `normalizeGenerated` | Typed legacy generative strategy helpers |
|
|
344
|
+
|
|
345
|
+
Import these as package subpaths under `@fayegram-ai/decision-bridge/`. All SDK
|
|
346
|
+
entry points are browser-safe ESM. Definition helpers preserve callback receivers
|
|
347
|
+
and validate/freeze wrappers; they do not implement a new protocol. Both kinds
|
|
348
|
+
work with the same bridge, extensions, cancellation, and batch API.
|
|
349
|
+
|
|
350
|
+
Generative results always use elicited probabilities and unavailable confidence.
|
|
351
|
+
The bridge rejects native provenance and answer confidence from that path.
|
|
352
|
+
Supplied probability/confidence provenance labels must be nonempty strings;
|
|
353
|
+
decision providers may preserve custom string labels. Generative prompt and
|
|
354
|
+
normalization helpers independently validate the shared-state, legacy-question
|
|
355
|
+
profile and explicitly reject unsupported request capabilities.
|
|
356
|
+
Decision-kind classification alone does not require confidence or known counts;
|
|
357
|
+
`validateResult({ native: true })` is a separate strict validation option used for
|
|
358
|
+
Jev's contract. See [custom decision](../guides/CUSTOM_DECISION.md),
|
|
359
|
+
[custom generative](../guides/CUSTOM_GENERATIVE.md), and the shared
|
|
360
|
+
[custom contract](../guides/CUSTOM_PROVIDERS.md).
|
|
361
|
+
|
|
362
|
+
## Validation guarantees
|
|
363
|
+
|
|
364
|
+
Legacy choice criteria maps allow at most 255 options, matching Jev's contract. Noul
|
|
365
|
+
criteria must be omitted or an object; its present true/false descriptions cannot
|
|
366
|
+
be raw null. Score levels likewise cannot be raw null (nested null JSON values are
|
|
367
|
+
still allowed). These invalid forms now fail before provider dispatch.
|
|
368
|
+
|
|
369
|
+
`decideChoice()` rejects unknown option names and abstention labels not present in
|
|
370
|
+
the answer. `evaluateBatch()` validates array holes as invalid requests before any
|
|
371
|
+
item is dispatched, rather than spending on adjacent valid entries.
|
|
372
|
+
|
|
373
|
+
The optional third argument to `validateResult()` must be a plain options object
|
|
374
|
+
with optional boolean `native` and integer `probabilityDecimalPlaces` (1–15) fields.
|
|
375
|
+
Providers can declare a pure synchronous `getResultValidationOptions(model)` hook;
|
|
376
|
+
its policy is validated before inference and is not read from response metadata.
|
|
377
|
+
Returning a Promise is a configuration error; its rejection is observed without
|
|
378
|
+
calling inference or reading a custom `then` accessor.
|
|
379
|
+
Cloudflare selects four-decimal rounding; other built-ins retain the 0.001 sum tolerance.
|
|
380
|
+
Unknown keys, accessors, symbol keys, non-enumerable fields, a non-boolean `native`,
|
|
381
|
+
or invalid probability precision are rejected as `CONFIGURATION_ERROR`.
|
|
382
|
+
An omitted or explicitly undefined `native` value keeps the default `false`.
|
|
383
|
+
Extension arrays must be dense; a hole is not an omitted extension.
|
|
384
|
+
|
|
385
|
+
Standalone `validateResult()` snapshots both the response and supplied questions.
|
|
386
|
+
Matching structured score rubrics compare by JSON content, including plain
|
|
387
|
+
objects with a null prototype. Invalid question JSON fails with `VALIDATION_ERROR`;
|
|
388
|
+
accessors are rejected without invocation. Question and result inputs are neither
|
|
389
|
+
mutated nor frozen.
|
|
390
|
+
|
|
391
|
+
Native provider cancellation/timeout errors preserve the last observed sanitized
|
|
392
|
+
request ID and attempt count when available. The bridge may settle its own
|
|
393
|
+
cancellation race before the provider returns diagnostics; do not require those
|
|
394
|
+
optional fields on every `ABORTED` or `TIMEOUT` error.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Primary protocol sources
|
|
2
|
+
|
|
3
|
+
These are the upstream protocol and platform references used by the bridge.
|
|
4
|
+
Recheck them before updating versions or claiming new compatibility. Listing a
|
|
5
|
+
reference does not imply that authenticated inference was run against its service.
|
|
6
|
+
|
|
7
|
+
| Subject | Official source |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| Node 22.16 JSON modules | https://nodejs.org/download/release/v22.16.0/docs/api/esm.html#json-modules |
|
|
10
|
+
| Node filesystem identities and atomic operations | https://nodejs.org/docs/latest-v22.x/api/fs.html |
|
|
11
|
+
| TypeSafe request/response API | https://docs.typesafe.ai/api |
|
|
12
|
+
| TypeSafe JavaScript examples | https://docs.typesafe.ai/sdk/javascript |
|
|
13
|
+
| Native model listing and aliases | https://docs.typesafe.ai/models |
|
|
14
|
+
| Choice decisions | https://docs.typesafe.ai/primitives/choice |
|
|
15
|
+
| Noul probabilities | https://docs.typesafe.ai/primitives/noul |
|
|
16
|
+
| Score rubric and weighted expectation | https://docs.typesafe.ai/primitives/score |
|
|
17
|
+
| Native confidence interpretation | https://docs.typesafe.ai/confidence |
|
|
18
|
+
| OpenAI structured output modes | https://platform.openai.com/docs/guides/structured-outputs |
|
|
19
|
+
| OpenAI native Decisions guide | https://developers.openai.com/api/docs/guides/decisions |
|
|
20
|
+
| OpenAI Decisions request/response contract | https://developers.openai.com/api/reference/resources/decisions/methods/create |
|
|
21
|
+
| OpenRouter structured output and routing | https://openrouter.ai/docs/guides/features/structured-outputs |
|
|
22
|
+
| Anthropic Messages, tools, usage, and stop reasons | https://platform.claude.com/docs/en/api/messages/create |
|
|
23
|
+
| GitHub checkout action | https://github.com/actions/checkout |
|
|
24
|
+
| GitHub Node setup action | https://github.com/actions/setup-node |
|
|
25
|
+
|
|
26
|
+
No model pricing, benchmark score, latency promise, or calibration guarantee is
|
|
27
|
+
baked into this implementation. The repository tests verify its own protocol and
|
|
28
|
+
software invariants using explicitly labeled fixtures. Live model behavior is a
|
|
29
|
+
separate operational check.
|
|
30
|
+
|
|
31
|
+
## Cloudflare decision inference
|
|
32
|
+
|
|
33
|
+
- [Clef](https://developers.cloudflare.com/workers-ai/models/clef/) and [Clef-flash](https://developers.cloudflare.com/workers-ai/models/clef-flash/)
|
|
34
|
+
- [Clef-omni](https://developers.cloudflare.com/workers-ai/models/clef-omni/) and its [input](https://developers.cloudflare.com/workers-ai/models/clef-omni/schema-input.json) / [output](https://developers.cloudflare.com/workers-ai/models/clef-omni/schema-output.json) schemas
|
|
35
|
+
- [Clef serving, pricing, and multimodal announcement](https://blog.cloudflare.com/clef-faster-cheaper-multimodal/)
|
|
36
|
+
- [Hosted input schema](https://developers.cloudflare.com/workers-ai/models/clef/schema-input.json) and [output schema](https://developers.cloudflare.com/workers-ai/models/clef/schema-output.json)
|
|
37
|
+
- [REST and permissions](https://developers.cloudflare.com/ai-gateway/usage/rest-api/)
|
|
38
|
+
- [Binding implementation and cancellation](https://github.com/cloudflare/workerd/blob/main/src/cloudflare/internal/ai-api.ts)
|
|
39
|
+
- [Gateway binding methods and options](https://developers.cloudflare.com/ai-gateway/usage/worker-binding-methods/)
|
|
40
|
+
- [Capacity rejection](https://developers.cloudflare.com/workers-ai/features/reject-if-busy/)
|
|
41
|
+
- [Gateway request handling](https://developers.cloudflare.com/ai-gateway/configuration/request-handling/)
|
|
42
|
+
- [Gateway limits](https://developers.cloudflare.com/ai-gateway/reference/limits/), including cacheable request size
|
|
43
|
+
- [Workers AI errors](https://developers.cloudflare.com/workers-ai/platform/errors/)
|
|
44
|
+
- [Cloudflare-owned model implementation](https://huggingface.co/Cloudflare/clef-flash/blob/main/joint_schema_model.py), including output precision
|
|
45
|
+
- [Cloudflare-owned Omni implementation](https://huggingface.co/Cloudflare/clef-omni/blob/main/joint_schema_model.py), including grouped media and output precision
|
|
46
|
+
|
|
47
|
+
## Package and repository release
|
|
48
|
+
|
|
49
|
+
- [Scoped public npm packages](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/)
|
|
50
|
+
- [npm staged publishing](https://docs.npmjs.com/staged-publishing/) and [stage commands](https://docs.npmjs.com/cli/v12/commands/npm-stage/)
|
|
51
|
+
- [GitHub private vulnerability reporting](https://docs.github.com/en/code-security/how-tos/report-and-fix-vulnerabilities/configure-vulnerability-reporting/configure-for-a-repository)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createBridge, choice, noul } from '../src/index.mjs';
|
|
2
|
+
|
|
3
|
+
const bridge = createBridge({ jev: {} }); // TYPESAFE_API_KEY; direct TypeSafe; jev-latest by default.
|
|
4
|
+
const result = await bridge.evaluate({
|
|
5
|
+
state: { error: "ModuleNotFoundError: No module named 'pytest'" },
|
|
6
|
+
questions: {
|
|
7
|
+
category: choice('Classify this failure from the supplied evidence.', {
|
|
8
|
+
dependency: 'A required package or module is unavailable.',
|
|
9
|
+
network: 'A network operation failed.',
|
|
10
|
+
assertion: 'A test assertion failed.',
|
|
11
|
+
unknown: 'Insufficient evidence for the other categories.',
|
|
12
|
+
}),
|
|
13
|
+
missing_dependency: noul('Does this error indicate a missing Python dependency?'),
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
console.log(JSON.stringify(result, null, 2));
|
|
17
|
+
// The caller decides what happens next. This example does not execute any action.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createBridge, evaluateBatch, noul } from '../src/index.mjs';
|
|
2
|
+
|
|
3
|
+
try {
|
|
4
|
+
const bridge = createBridge({ jev: {} });
|
|
5
|
+
const questions = { failure: noul('Does the evidence explicitly report a failed test?') };
|
|
6
|
+
const requests = ['18 passed, 2 failed.', 'Tests were not run.'].map(state => ({ state, questions }));
|
|
7
|
+
const records = await evaluateBatch(bridge, requests, { concurrency: 2 });
|
|
8
|
+
console.log(JSON.stringify(records, null, 2));
|
|
9
|
+
if (records.some(record => !record.ok)) process.exitCode = 1;
|
|
10
|
+
} catch (error) {
|
|
11
|
+
console.error(error.code ?? 'ERROR', error.message);
|
|
12
|
+
process.exitCode = 1;
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Run explicitly with: node --env-file=.env examples/cloudflare-decisions.mjs
|
|
2
|
+
// These two hosted inference calls may incur charges.
|
|
3
|
+
import { createBridge, CloudflareDecisionsProvider, choice, noul, publicError } from '../src/index.mjs';
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
const provider = new CloudflareDecisionsProvider({ model: process.env.DECISION_MODEL ?? 'clef' });
|
|
7
|
+
const bridge = createBridge({ provider });
|
|
8
|
+
const state = { ticket: 'The production checkout is unavailable.' };
|
|
9
|
+
const common = await bridge.evaluate({ state, questions: {
|
|
10
|
+
urgent: noul('Does this need immediate attention?'),
|
|
11
|
+
route: choice('Choose a route.', [{ value: true, description: 'Immediate incident response.' },
|
|
12
|
+
{ value: 'review', description: 'Normal review.' }]),
|
|
13
|
+
} });
|
|
14
|
+
console.log(JSON.stringify(common, null, 2));
|
|
15
|
+
const native = await provider.createDecision({ state, questions: {
|
|
16
|
+
urgent: { type: 'noul', instructions: 'Does this need immediate attention?' },
|
|
17
|
+
} });
|
|
18
|
+
console.log(JSON.stringify(native, null, 2));
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error(publicError(error));
|
|
21
|
+
process.exitCode = 1;
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Run: node --env-file=.env examples/cloudflare-omni.mjs clip.wav clip.mp4
|
|
2
|
+
// Makes two hosted inference calls. Supply valid local WAV and MP4 fixtures.
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { createBridge, CloudflareDecisionsProvider, noul, publicError } from '../src/index.mjs';
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
const [audioPath, videoPath] = process.argv.slice(2);
|
|
8
|
+
if (!audioPath || !videoPath) throw new Error('Supply a WAV path and an MP4 path.');
|
|
9
|
+
const [audio, video] = await Promise.all([readFile(audioPath), readFile(videoPath)]);
|
|
10
|
+
const audioUrl = 'data:audio/wav;base64,' + audio.toString('base64');
|
|
11
|
+
const videoUrl = 'data:video/mp4;base64,' + video.toString('base64');
|
|
12
|
+
const provider = new CloudflareDecisionsProvider({ model: 'clef-omni' });
|
|
13
|
+
const bridge = createBridge({ provider });
|
|
14
|
+
const common = await bridge.evaluate({
|
|
15
|
+
input: [
|
|
16
|
+
{ type: 'audio', dataUrl: audioUrl },
|
|
17
|
+
{ type: 'video', dataUrl: videoUrl },
|
|
18
|
+
{ type: 'text', text: 'Assess these clips.' },
|
|
19
|
+
],
|
|
20
|
+
questions: { speech: noul('Is audible speech present?') },
|
|
21
|
+
});
|
|
22
|
+
const native = await provider.createDecision({
|
|
23
|
+
state: { purpose: 'Assess these clips.' },
|
|
24
|
+
audio: [audioUrl],
|
|
25
|
+
videos: [videoUrl],
|
|
26
|
+
questions: { speech: { type: 'noul', instructions: 'Is audible speech present?' } },
|
|
27
|
+
});
|
|
28
|
+
console.log(JSON.stringify({ common, native }, null, 2));
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(publicError(error));
|
|
31
|
+
process.exitCode = 1;
|
|
32
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Integrate this handler into an existing Worker with a configured AI binding.
|
|
2
|
+
// Authorize callers and cap incoming HTTP bodies at 13 MiB before this handler.
|
|
3
|
+
// For Omni, select clef-omni and use its configured budget (36 MiB by default).
|
|
4
|
+
// SDK preflight runs after JSON parsing; it does not protect unbounded HTTP intake.
|
|
5
|
+
import { createBridge, CloudflareDecisionsProvider, publicError } from '../src/index.mjs';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
async fetch(request, env) {
|
|
9
|
+
if (request.method !== 'POST') return new Response('Use POST with a common decision request.', { status: 405 });
|
|
10
|
+
try {
|
|
11
|
+
const provider = new CloudflareDecisionsProvider({ transport: 'binding', ai: env.AI, model: 'clef' });
|
|
12
|
+
const bridge = createBridge({ provider });
|
|
13
|
+
const result = await bridge.evaluate(await request.json(), { signal: request.signal });
|
|
14
|
+
return Response.json(result);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
const details = publicError(error);
|
|
17
|
+
const invalid = ['VALIDATION_ERROR', 'UNSUPPORTED_CAPABILITY', 'REQUEST_TOO_LARGE'].includes(details.code);
|
|
18
|
+
return Response.json({ error: details }, { status: invalid ? 400 : 502 });
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"model": "clef",
|
|
3
|
+
"state": {
|
|
4
|
+
"ticket": "The production checkout is unavailable."
|
|
5
|
+
},
|
|
6
|
+
"questions": {
|
|
7
|
+
"urgent": {
|
|
8
|
+
"type": "noul",
|
|
9
|
+
"instructions": "Does this need immediate attention?"
|
|
10
|
+
},
|
|
11
|
+
"route": {
|
|
12
|
+
"type": "choice",
|
|
13
|
+
"instructions": "Choose a route.",
|
|
14
|
+
"criteria": {
|
|
15
|
+
"incident": "Immediate incident response.",
|
|
16
|
+
"review": "Normal review."
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"severity": {
|
|
20
|
+
"type": "score",
|
|
21
|
+
"instructions": "Rate severity.",
|
|
22
|
+
"criteria": [
|
|
23
|
+
"No production impact.",
|
|
24
|
+
"Production functionality is unavailable."
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../schemas/decision.config.schema.json",
|
|
3
|
+
"schemaVersion": 1,
|
|
4
|
+
"provider": "custom-generative",
|
|
5
|
+
"module": "../custom/generative-provider.mjs",
|
|
6
|
+
"model": "YOUR_SERVED_MODEL",
|
|
7
|
+
"apiKeyEnv": "PROJECT_MODEL_KEY",
|
|
8
|
+
"options": {
|
|
9
|
+
"baseURL": "https://your-model-service.example/v1",
|
|
10
|
+
"responseFormat": "json_schema",
|
|
11
|
+
"tokenParameter": "max_tokens"
|
|
12
|
+
},
|
|
13
|
+
"timeoutMs": 30000
|
|
14
|
+
}
|