@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,164 @@
|
|
|
1
|
+
# Custom generative providers
|
|
2
|
+
|
|
3
|
+
This opt-in path elicits decisions from general-purpose models. It retains the
|
|
4
|
+
maintained OpenAI-compatible, OpenRouter, and Anthropic adapters. Import them from
|
|
5
|
+
`@fayegram-ai/decision-bridge/custom/generative`; the root factory accepts native
|
|
6
|
+
decision providers only.
|
|
7
|
+
|
|
8
|
+
## Use a maintained preset
|
|
9
|
+
|
|
10
|
+
```javascript
|
|
11
|
+
import { createBridge, noul } from '@fayegram-ai/decision-bridge';
|
|
12
|
+
import { createGenerativeProvider } from '@fayegram-ai/decision-bridge/custom/generative';
|
|
13
|
+
|
|
14
|
+
const provider = createGenerativeProvider({
|
|
15
|
+
type: 'openrouter',
|
|
16
|
+
model: process.env.DECISION_MODEL, // A model available through your account.
|
|
17
|
+
});
|
|
18
|
+
const bridge = createBridge({ provider });
|
|
19
|
+
const result = await bridge.evaluate({
|
|
20
|
+
state: 'Test report: two assertions failed.',
|
|
21
|
+
questions: { failure: noul('Does the report explicitly state that a test failed?') },
|
|
22
|
+
});
|
|
23
|
+
console.log(result.meta.providerKind); // generative
|
|
24
|
+
console.log(result.meta.probabilitySource); // elicited
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Set `OPENROUTER_API_KEY` and a real `DECISION_MODEL`, then load your environment
|
|
28
|
+
explicitly with Node or your application's configuration. Evaluation is a real
|
|
29
|
+
service call. Presets have no default model. The classes `OpenAICompatibleProvider`,
|
|
30
|
+
`OpenRouterProvider`, and `AnthropicProvider` and their option types are exported
|
|
31
|
+
from the same subpath.
|
|
32
|
+
|
|
33
|
+
## CLI presets
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm exec -- decision-bridge init ./my-model --provider custom-generative --preset openrouter --model VENDOR/MODEL_ID
|
|
37
|
+
npm exec -- decision-bridge evaluate ./my-model/request.json --config ./my-model/decision.config.json --env-file .env
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Use `--preset openai-compatible` or `--preset anthropic` for the other protocols.
|
|
41
|
+
A configuration selects exactly one preset or module:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"schemaVersion": 1,
|
|
46
|
+
"provider": "custom-generative",
|
|
47
|
+
"preset": "openai-compatible",
|
|
48
|
+
"model": "YOUR_MODEL_ID",
|
|
49
|
+
"apiKeyEnv": "OPENAI_API_KEY",
|
|
50
|
+
"responseFormat": "json_schema",
|
|
51
|
+
"timeoutMs": 30000
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For a loopback server, set `baseURL`, `noAuth: true`, the served model, and the
|
|
56
|
+
appropriate `tokenParameter`. Remote custom origins require explicit credentials.
|
|
57
|
+
See [configuration](../reference/CONFIGURATION.md).
|
|
58
|
+
|
|
59
|
+
## A new protocol
|
|
60
|
+
|
|
61
|
+
Use `defineGenerativeProvider` for a module implementing a new wire protocol.
|
|
62
|
+
`init --provider custom-generative` creates a module with a clear unimplemented
|
|
63
|
+
service call; it never produces synthetic decisions as a fallback.
|
|
64
|
+
|
|
65
|
+
The subpath exports `buildResponseSchema(questions)`, `buildPrompt(request)`,
|
|
66
|
+
`parseGeneratedJson(text)`, and `normalizeGenerated(payload, request, options)`.
|
|
67
|
+
Build a prompt/schema from a validated, resolved legacy request, send it using
|
|
68
|
+
transport you own, parse the generated JSON, then normalize it. The normalizer
|
|
69
|
+
expects exactly `{ answers }`: each `noul` answer contains `{ noul }`, and each
|
|
70
|
+
choice/score contains `{ probabilities }` with exactly the supplied option keys
|
|
71
|
+
or zero-based level indices. It checks probabilities, derives argmax/weighted
|
|
72
|
+
scores and matching legends, and marks absent usage as unknown. It does not repair
|
|
73
|
+
JSON, strip Markdown, or create confidence. `buildPrompt` and `normalizeGenerated`
|
|
74
|
+
independently validate and snapshot the request. They require shared state and
|
|
75
|
+
legacy questions, and reject ordered input, typed choices, labelled scores, and
|
|
76
|
+
safety identifiers with `UNSUPPORTED_CAPABILITY`. Malformed request JSON raises
|
|
77
|
+
`VALIDATION_ERROR`; unsupported input is never silently discarded. New modules
|
|
78
|
+
implement their own encoding and normalization for richer capabilities.
|
|
79
|
+
|
|
80
|
+
| Helper | Return value and responsibility |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `buildResponseSchema(questions)` | JSON Schema for legacy question answers; validates the definitions without selecting an inference model |
|
|
83
|
+
| `buildPrompt(request)` | `{ system, user }` strings for a resolved shared-state request; requires a model in the request |
|
|
84
|
+
| `parseGeneratedJson(text)` | Parsed JSON; rejects empty or malformed content with `INVALID_RESPONSE`; answer validation happens in normalization |
|
|
85
|
+
| `normalizeGenerated(payload, request, options?)` | Canonical, validated `{ model, answers, usage, meta }`; returns a snapshot that the bridge subsequently freezes |
|
|
86
|
+
|
|
87
|
+
The normalizer's optional third argument accepts `model`, `usage`, and `meta`:
|
|
88
|
+
|
|
89
|
+
| Field | Meaning |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `model` | Reported nonempty model ID; omission or null uses `request.model` and marks `modelSource: 'requested'` |
|
|
92
|
+
| `usage` | Validated common token counts, with null for unreported counts; omission produces null input/output counts |
|
|
93
|
+
| `meta` | Additional JSON metadata from your transport; normalization supplies authoritative `requestedModel`, `modelSource`, `probabilitySource: 'elicited'`, and `confidenceSource: 'unavailable'` |
|
|
94
|
+
|
|
95
|
+
None of these helpers sends a request. Your provider supplies the transport,
|
|
96
|
+
cancellation, response extraction, and safe error handling. Use the
|
|
97
|
+
[common usage and metadata contract](../reference/SDK.md#returned-data) for the
|
|
98
|
+
reported fields; the bridge adds its own classification and final timings.
|
|
99
|
+
|
|
100
|
+
A runnable delegation module is provided at
|
|
101
|
+
[examples/custom/generative-provider.mjs](../../examples/custom/generative-provider.mjs).
|
|
102
|
+
Use the shared [module contract](CUSTOM_PROVIDERS.md) for loader options, timeouts,
|
|
103
|
+
capabilities, and testing requirements. No arbitrary declarative HTTP mapping
|
|
104
|
+
language is supported.
|
|
105
|
+
|
|
106
|
+
## Result semantics
|
|
107
|
+
|
|
108
|
+
These providers have `kind: 'generative'`. The bridge requires elicited probability
|
|
109
|
+
provenance and `confidenceSource: 'unavailable'`, and rejects returned native
|
|
110
|
+
confidence claims or answer confidence fields. Their output is an estimate elicited
|
|
111
|
+
from a model, not a native probability, logprob, or calibration guarantee.
|
|
112
|
+
Presets support shared state and legacy `choice`, `noul`, and `score`; ordered
|
|
113
|
+
media, typed choices, labelled scores, and safety identifiers are unsupported.
|
|
114
|
+
A new module may declare capabilities only after implementing and validating them.
|
|
115
|
+
|
|
116
|
+
## OpenAI-compatible servers
|
|
117
|
+
|
|
118
|
+
Default base URL: `https://api.openai.com/v1`. Base paths such as `/v1` are preserved;
|
|
119
|
+
provide the API base, not the final `/chat/completions` path.
|
|
120
|
+
|
|
121
|
+
`json_schema` sends `response_format.type = json_schema`, `strict: true`, and a
|
|
122
|
+
schema whose required object fields disallow additional properties. Ranges and
|
|
123
|
+
normalization are validated locally to accommodate different supported schema
|
|
124
|
+
subsets. `json_object` requests JSON without schema enforcement. `text` omits the
|
|
125
|
+
format field and relies on instructions plus strict local validation.
|
|
126
|
+
|
|
127
|
+
There is **no auto mode**: an unsupported schema error is visible rather than
|
|
128
|
+
triggering a second request with weaker enforcement. A refusal, truncated output,
|
|
129
|
+
malformed JSON, probability violation, or unexpected tool call is an explicit
|
|
130
|
+
error. No Markdown stripping or JSON repair occurs.
|
|
131
|
+
|
|
132
|
+
The default token field is `max_completion_tokens`. Some compatible servers need
|
|
133
|
+
`max_tokens`; set `tokenParameter` explicitly. No temperature or reasoning setting
|
|
134
|
+
is forced. Additional generation parameters must be supported by the selected
|
|
135
|
+
model. Presets reserve current and legacy tool/function controls, including
|
|
136
|
+
`functions` and `function_call`, to preserve their answer-output protocol. Use a
|
|
137
|
+
custom module when you need to implement a different protocol.
|
|
138
|
+
This provider does not implement OpenAI Responses API, realtime streaming,
|
|
139
|
+
multimodal inputs, or a general function-calling agent loop.
|
|
140
|
+
|
|
141
|
+
## OpenRouter
|
|
142
|
+
|
|
143
|
+
`OpenRouterProvider` is a fixed-endpoint generative preset. It reads its own key
|
|
144
|
+
and defaults to `max_tokens`. It sets
|
|
145
|
+
`parameters.provider.require_parameters = true` so required request features are
|
|
146
|
+
not silently ignored by routing. Other explicit routing settings are preserved.
|
|
147
|
+
The preset does not guarantee which upstream host serves a model; use OpenRouter's
|
|
148
|
+
routing controls for stricter requirements.
|
|
149
|
+
|
|
150
|
+
Select a **generative model** that supports the chosen output mode. Other API
|
|
151
|
+
protocols require an explicit [custom provider](CUSTOM_PROVIDERS.md).
|
|
152
|
+
|
|
153
|
+
## Anthropic
|
|
154
|
+
|
|
155
|
+
The provider sends Messages API headers `x-api-key` and
|
|
156
|
+
`anthropic-version: 2023-06-01`. It defines a synthetic `submit_decisions` tool whose
|
|
157
|
+
input schema is the expected answer object, then forces exactly that tool. The
|
|
158
|
+
returned `tool_use.input` carries the data; **no tool is executed** and no follow-up
|
|
159
|
+
agent cycle is started.
|
|
160
|
+
|
|
161
|
+
Extended thinking is not enabled in this forced-tool strategy; a `thinking`
|
|
162
|
+
parameter is rejected. Refusals, truncated output, unexpected stop reasons, and
|
|
163
|
+
missing/extra named tools fail. `GET /v1/models` returns the first page with
|
|
164
|
+
`has_more` / `last_id` preserved. Automatic pagination is not implemented.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Custom provider contracts
|
|
2
|
+
|
|
3
|
+
Decision Bridge has one bridge and two explicit extension paths:
|
|
4
|
+
|
|
5
|
+
| Path | SDK helper | CLI provider | Purpose |
|
|
6
|
+
| --- | --- | --- | --- |
|
|
7
|
+
| [Custom decision](CUSTOM_DECISION.md) | `defineDecisionProvider` from `custom/decision` | `custom-decision` with `module` | A new decision model protocol |
|
|
8
|
+
| [Custom generative](CUSTOM_GENERATIVE.md) | `defineGenerativeProvider` from `custom/generative` | `custom-generative` with `preset` or `module` | Elicit structured decisions from a general-purpose model |
|
|
9
|
+
|
|
10
|
+
The root `createProvider()` factory supports native Jev, OpenAI Decisions, and
|
|
11
|
+
Cloudflare only. `createGenerativeProvider()` selects maintained generative
|
|
12
|
+
presets. Select the corresponding custom entry point explicitly for either
|
|
13
|
+
extension path.
|
|
14
|
+
|
|
15
|
+
## Common SDK contract
|
|
16
|
+
|
|
17
|
+
`Provider` requires a nonempty `name`, explicit `kind: 'decision' | 'generative'`,
|
|
18
|
+
and `evaluate(request, { signal? })`. Optional fields and hooks are:
|
|
19
|
+
|
|
20
|
+
- `defaultModel`: a nonempty model ID, otherwise the request must supply one.
|
|
21
|
+
- `probabilitySource`: known provenance; generative providers use `elicited`.
|
|
22
|
+
- `getCapabilities(model)`: a pure synchronous capability profile.
|
|
23
|
+
- `validateRequest(request)`: pure synchronous provider preflight; no inference.
|
|
24
|
+
- `getResultValidationOptions(model)`: pure synchronous probability precision
|
|
25
|
+
policy, never derived from a response or its metadata.
|
|
26
|
+
- `listModels({ signal? })`: a model catalog when the service implements one.
|
|
27
|
+
|
|
28
|
+
Both definition helpers validate plain data definitions and freeze their wrappers.
|
|
29
|
+
Callbacks keep their original receiver. The bridge also accepts provider classes
|
|
30
|
+
that implement this contract directly. Missing or invalid kinds fail at construction.
|
|
31
|
+
`bridge.providerKind` and `result.meta.providerKind` come from the contract and
|
|
32
|
+
cannot be overridden by response metadata.
|
|
33
|
+
|
|
34
|
+
The request is already snapshotted, validated, resolved to a model, and frozen.
|
|
35
|
+
Return canonical `{ model, answers, usage, meta? }`; the bridge validates and
|
|
36
|
+
freezes the result. Custom providers own API encoding, transport, response mapping,
|
|
37
|
+
credential destinations, bounded bodies, safe errors, and cancellation cleanup.
|
|
38
|
+
Internal transport helpers are not stable public exports. Delegate a maintained
|
|
39
|
+
preset for a protocol already supported, or implement and test your own transport.
|
|
40
|
+
|
|
41
|
+
Capability omissions retain the legacy shared-state and question profile. Audio
|
|
42
|
+
and video additions default to false/zero. Declare richer forms only when their
|
|
43
|
+
encoding and validation are implemented. The bridge runs preflight again after
|
|
44
|
+
preparation hooks. Unknown usage is null; no provider should invent token counts,
|
|
45
|
+
confidence, or calibration. Generative native-provenance claims are rejected.
|
|
46
|
+
Supplied `probabilitySource` and `confidenceSource` metadata must be nonempty
|
|
47
|
+
strings. Decision providers may use custom labels; omitted probability provenance
|
|
48
|
+
uses the provider's declared value, or `unspecified` when none is declared.
|
|
49
|
+
|
|
50
|
+
## Explicit CLI modules
|
|
51
|
+
|
|
52
|
+
Select a custom kind and `module` in JSON, or pass both `--provider custom-decision`
|
|
53
|
+
(or `custom-generative`) and `--provider-module FILE`. A module flag does not infer
|
|
54
|
+
a kind. A generative profile chooses exactly one `preset` or `module`; module
|
|
55
|
+
profiles require JSON `options` for implementation-specific transport/tuning.
|
|
56
|
+
Inline `apiKey`/`api_key` options are rejected; use top-level `apiKeyEnv`.
|
|
57
|
+
|
|
58
|
+
The module exports `createProvider(options)`, synchronously or asynchronously.
|
|
59
|
+
Import/factory waiting is bounded by `timeoutMs` (30 seconds by default) and CLI
|
|
60
|
+
cancellation, separately from evaluation. The loader passes JSON `options`, then
|
|
61
|
+
the selected `apiKey` and `model` when present. Its returned kind must match the
|
|
62
|
+
selected path. The CLI also applies its configured model to common requests that
|
|
63
|
+
omit a model, independently of the factory's own default; an explicit request
|
|
64
|
+
model takes precedence, and `--model` overrides both. Module paths in JSON resolve
|
|
65
|
+
relative to that file; flag paths
|
|
66
|
+
resolve relative to the working directory. Explicit modules execute trusted code;
|
|
67
|
+
this extension point is not a sandbox. No module is discovered automatically.
|
|
68
|
+
|
|
69
|
+
`init` creates an honest service implementation placeholder when no implementation
|
|
70
|
+
is selected. When given an existing module, it references that module without
|
|
71
|
+
loading or overwriting it. Offline `validate` never imports custom modules or
|
|
72
|
+
requires credentials; it reports structure-only validation for unknown protocols.
|
|
73
|
+
`bridge.validate()` on an instantiated provider includes capabilities and preflight.
|
|
74
|
+
|
|
75
|
+
## Tests for a new protocol
|
|
76
|
+
|
|
77
|
+
Test serialized bodies, credentials/headers, endpoint paths, all supported question
|
|
78
|
+
mappings, usage/provenance, refusals/truncation, malformed distributions, timeouts,
|
|
79
|
+
cancellation, and safe errors. Use local HTTP fixtures for deterministic protocol
|
|
80
|
+
checks. Live tests must be explicit and credential-dependent; fixture tests do
|
|
81
|
+
not establish model quality. See [testing](../development/TESTING.md).
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
Use the SDK from your JavaScript or TypeScript application to submit `state` and
|
|
4
|
+
`questions` and receive structured answers. The optional command runner can also
|
|
5
|
+
evaluate request files.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
This guide's example uses Node.js **22.16 or newer**, npm, and a TypeSafe API key
|
|
10
|
+
for the explicitly selected Jev provider. The SDK also loads in modern browsers; direct calls
|
|
11
|
+
depend on the provider's CORS policy and an explicit client-safe credential. See
|
|
12
|
+
[browser integration](BROWSER.md). Other backends use their own keys; see
|
|
13
|
+
[switching models](SWITCHING_MODELS.md).
|
|
14
|
+
|
|
15
|
+
## Install into your application
|
|
16
|
+
|
|
17
|
+
In your application's directory, install the package from npm:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @fayegram-ai/decision-bridge
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
You can also install a verified local archive, replacing the path below. See
|
|
24
|
+
[packaging and release](../development/RELEASING.md) to build and verify one from
|
|
25
|
+
a source checkout:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install /path/to/fayegram-ai-decision-bridge-0.1.0.tgz
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The installed package contains the SDK, declarations, command runner, examples,
|
|
32
|
+
and docs. It runs without compilation; source development scripts and tests are
|
|
33
|
+
excluded.
|
|
34
|
+
|
|
35
|
+
## Configure credentials
|
|
36
|
+
|
|
37
|
+
Create `.env` in your application's directory and edit it locally:
|
|
38
|
+
|
|
39
|
+
```dotenv
|
|
40
|
+
TYPESAFE_API_KEY=your_real_key_here
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Add `.env` to your application's `.gitignore`. The SDK reads `TYPESAFE_API_KEY`
|
|
44
|
+
from the process environment when constructing the selected Jev provider. It does not load
|
|
45
|
+
`.env` itself; the Node startup command below loads it explicitly. In a deployed
|
|
46
|
+
application, you can supply the same variable through your runtime's environment.
|
|
47
|
+
|
|
48
|
+
## Make your first SDK call
|
|
49
|
+
|
|
50
|
+
Save this as `app.mjs` beside `.env`. The `.mjs` extension enables ESM regardless
|
|
51
|
+
of your application's `package.json` settings:
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
import { createBridge, choice, noul, publicError } from '@fayegram-ai/decision-bridge';
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const bridge = createBridge({ jev: {} }); // Jev; defaults to model jev-latest.
|
|
58
|
+
const result = await bridge.evaluate({
|
|
59
|
+
state: { message: 'I was charged twice for the same invoice.' },
|
|
60
|
+
questions: {
|
|
61
|
+
route: choice('Which team should inspect this message?', {
|
|
62
|
+
billing: 'Invoices, charges, or payment problems.',
|
|
63
|
+
technical: 'Software defects or connectivity problems.',
|
|
64
|
+
unknown: 'The evidence does not establish a team.',
|
|
65
|
+
}),
|
|
66
|
+
duplicate_charge: noul('Does the message report a duplicate charge?'),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
console.log(result.answers.route.choice);
|
|
70
|
+
console.log(result.answers.duplicate_charge.noul);
|
|
71
|
+
console.log(result.model, result.meta.probabilitySource);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error(publicError(error));
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Run it from your application directory:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
node --env-file=.env app.mjs
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This sends one real evaluation and may incur charges. `route.choice` is one of
|
|
85
|
+
`billing`, `technical`, or `unknown`; `duplicate_charge.noul` is a probability
|
|
86
|
+
between 0 and 1. `result.model` identifies the returned model, and Jev marks its
|
|
87
|
+
probability source as `native`. Predictions depend on the model. Your application
|
|
88
|
+
decides how to use them; the SDK does not route messages or execute actions.
|
|
89
|
+
|
|
90
|
+
Change `state` and the question instructions/criteria on each call. No prompt
|
|
91
|
+
registration is required. Reuse the bridge for subsequent requests. To choose
|
|
92
|
+
a Jev model explicitly, construct it with `createBridge({ jev: { model: 'YOUR_MODEL' } })`,
|
|
93
|
+
using an ID available to your account. An individual request's `model` overrides
|
|
94
|
+
that default. See the [SDK reference](../reference/SDK.md) for scores, result
|
|
95
|
+
metadata, cancellation, batching, and extensions.
|
|
96
|
+
|
|
97
|
+
Bridge construction can fail for invalid configuration or missing credentials;
|
|
98
|
+
evaluation can fail for invalid input or provider errors. Both are inside the
|
|
99
|
+
example's `try` block. `publicError(error)` returns a safe `code` and `message`, plus
|
|
100
|
+
HTTP status, request ID, and attempt count when available. See [errors and recovery](../reference/ERRORS.md).
|
|
101
|
+
|
|
102
|
+
SDK selection is explicit: use `createBridge({ provider })` with a constructed
|
|
103
|
+
provider or `createBridge({ jev: options })` for Jev. Empty options and omitted
|
|
104
|
+
options are errors. `createProvider` also requires its `type` explicitly.
|
|
105
|
+
The CLI has its own Jev default and does not change SDK selection rules.
|
|
106
|
+
|
|
107
|
+
## TypeScript
|
|
108
|
+
|
|
109
|
+
Declarations ship with the package. For a standalone Node TypeScript example,
|
|
110
|
+
install these development tools in the consuming application (TypeScript 5.8.3
|
|
111
|
+
is the compiler used by this repository's CI):
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm install --save-dev typescript@5.8.3 @types/node@22
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Create `tsconfig.json`, or incorporate these settings into your existing config:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"compilerOptions": {
|
|
122
|
+
"target": "ES2022",
|
|
123
|
+
"module": "NodeNext",
|
|
124
|
+
"moduleResolution": "NodeNext",
|
|
125
|
+
"strict": true,
|
|
126
|
+
"outDir": "dist",
|
|
127
|
+
"types": ["node"]
|
|
128
|
+
},
|
|
129
|
+
"include": ["app.mts"]
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Save this as `app.mts`. The `.mts` extension makes it an ESM module and produces
|
|
134
|
+
`dist/app.mjs`; no `"type": "module"` setting is needed for this example:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { createBridge, choice, publicError } from '@fayegram-ai/decision-bridge';
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const bridge = createBridge({ jev: {} });
|
|
141
|
+
const result = await bridge.evaluate({
|
|
142
|
+
state: 'An invoice appears twice.',
|
|
143
|
+
questions: {
|
|
144
|
+
route: choice('Which team should inspect this?', {
|
|
145
|
+
billing: 'An invoice or payment issue.',
|
|
146
|
+
unknown: 'Insufficient evidence.',
|
|
147
|
+
}),
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
// The builder preserves the allowed labels in the answer type.
|
|
151
|
+
const route: 'billing' | 'unknown' = result.answers.route.choice;
|
|
152
|
+
const probability: number = result.answers.route.probabilities[route];
|
|
153
|
+
console.log(route, probability);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error(publicError(error));
|
|
156
|
+
process.exitCode = 1;
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Compile, then run with the same environment file:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npm exec -- tsc --project tsconfig.json
|
|
164
|
+
node --env-file=.env dist/app.mjs
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Existing CommonJS applications
|
|
168
|
+
|
|
169
|
+
Use dynamic import from an async function in a `.cjs` module:
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
async function main() {
|
|
173
|
+
const { createBridge, noul } = await import('@fayegram-ai/decision-bridge');
|
|
174
|
+
const bridge = createBridge({ jev: {} });
|
|
175
|
+
const result = await bridge.evaluate({
|
|
176
|
+
state: 'The test log reports a failure.',
|
|
177
|
+
questions: { failed: noul('Does the log report a failed test?') },
|
|
178
|
+
});
|
|
179
|
+
console.log(result.answers.failed.noul);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
main().catch(() => {
|
|
183
|
+
console.error('Evaluation failed. Check credentials and request configuration.');
|
|
184
|
+
process.exitCode = 1;
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Save it as `app.cjs` and run `node --env-file=.env app.cjs`. The package exposes an
|
|
189
|
+
ESM import entry; it does not provide a native `require()` entry.
|
|
190
|
+
|
|
191
|
+
## Optional command runner
|
|
192
|
+
|
|
193
|
+
After a local package installation, use `npm exec` from the consuming project:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
npm exec -- decision-bridge init ./my-decisions
|
|
197
|
+
npm exec -- decision-bridge validate ./my-decisions/request.json
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
`init` creates `decision.config.json`, `decision.config.schema.json`, `request.json`,
|
|
201
|
+
`.env.example`, and `.gitignore`; existing files are never overwritten. The config's
|
|
202
|
+
`$schema` points to the copied schema for editor completion. `validate` checks the request offline.
|
|
203
|
+
Copy the generated `.env.example` to `.env` inside `my-decisions`, then edit its key.
|
|
204
|
+
Evaluate the generated request with explicit paths:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
npm exec -- decision-bridge evaluate ./my-decisions/request.json --config ./my-decisions/decision.config.json --env-file ./my-decisions/.env
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The command runner does not discover `.env` or configuration files automatically.
|
|
211
|
+
Paths are relative to the working directory, except custom provider module paths
|
|
212
|
+
inside a config file, which are relative to that file. The [command reference](../reference/CLI.md)
|
|
213
|
+
covers commands, flags, streams, and exit codes.
|
|
214
|
+
|
|
215
|
+
Global installation is optional:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
npm install --global @fayegram-ai/decision-bridge
|
|
219
|
+
decision-bridge --version
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The package also installs `decision` and `faye-decision` as equivalent CLI aliases.
|
|
223
|
+
For example, `npm exec -- faye-decision --version` runs the same command runner.
|
|
224
|
+
The shorter `decision` alias may collide with an existing executable; prefer
|
|
225
|
+
`decision-bridge` or `faye-decision` in automation.
|
|
226
|
+
|
|
227
|
+
## Run from a source checkout
|
|
228
|
+
|
|
229
|
+
For repository development, open a terminal at the source root. Copy `.env.example`
|
|
230
|
+
to `.env` (`cp .env.example .env` on macOS/Linux or `Copy-Item .env.example .env`
|
|
231
|
+
in PowerShell), then edit the key locally. Run:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
npm ci --ignore-scripts
|
|
235
|
+
npm test
|
|
236
|
+
npm run validate
|
|
237
|
+
npm run models
|
|
238
|
+
npm run ask
|
|
239
|
+
npm run example
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`npm ci --ignore-scripts` installs the pinned development dependencies from the
|
|
243
|
+
lockfile; it can access the npm registry and requires no model credentials.
|
|
244
|
+
`npm test` and `npm run validate` are offline. `models` lists account models; `ask` evaluates
|
|
245
|
+
`examples/request.json`; `example` runs the source SDK example. The last three
|
|
246
|
+
explicitly load `.env` when present and access the provider. A model listing does
|
|
247
|
+
not prove inference access; evaluation verifies that path.
|
|
248
|
+
|
|
249
|
+
Source examples import `../src/index.mjs` and run directly without installing the
|
|
250
|
+
package. Application code should use `@fayegram-ai/decision-bridge` as shown above.
|
|
251
|
+
See [platform setup](PLATFORMS.md) for operating-system details and
|
|
252
|
+
[contributor documentation](../development/README.md) for development checks.
|