@fayegram-ai/decision-bridge 0.1.2 → 0.1.3

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 (72) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/CONTRIBUTING.md +5 -3
  3. package/README.md +24 -8
  4. package/SECURITY.md +18 -11
  5. package/docs/README.md +17 -4
  6. package/docs/development/ARCHITECTURE.md +32 -5
  7. package/docs/development/DESIGN.md +21 -3
  8. package/docs/development/RELEASING.md +7 -3
  9. package/docs/development/REPOSITORY_LAYOUT.md +2 -0
  10. package/docs/development/TESTING.md +31 -3
  11. package/docs/guides/BROWSER.md +5 -0
  12. package/docs/guides/CLOUDFLARE_DECISIONS.md +24 -46
  13. package/docs/guides/CUSTOMIZATION.md +8 -2
  14. package/docs/guides/CUSTOM_GENERATIVE.md +5 -3
  15. package/docs/guides/CUSTOM_PROVIDERS.md +5 -3
  16. package/docs/guides/EXTRACTION.md +125 -0
  17. package/docs/guides/GETTING_STARTED.md +8 -4
  18. package/docs/guides/JEV.md +13 -20
  19. package/docs/guides/LIVE_DECISIONS.md +163 -0
  20. package/docs/guides/OPENAI_DECISIONS.md +52 -24
  21. package/docs/guides/PLATFORMS.md +3 -3
  22. package/docs/guides/SWITCHING_MODELS.md +6 -2
  23. package/docs/guides/TROUBLESHOOTING.md +25 -0
  24. package/docs/reference/CLI.md +48 -10
  25. package/docs/reference/CONFIGURATION.md +31 -8
  26. package/docs/reference/ERRORS.md +41 -12
  27. package/docs/reference/PROVIDERS.md +19 -6
  28. package/docs/reference/SDK.md +62 -24
  29. package/docs/reference/SOURCES.md +7 -0
  30. package/examples/extraction.mjs +16 -0
  31. package/examples/live-decisions.mjs +24 -0
  32. package/package.json +11 -1
  33. package/schemas/decision.config.schema.json +88 -11
  34. package/src/bridge.mjs +9 -6
  35. package/src/capabilities.mjs +28 -4
  36. package/src/cli/config-validation.mjs +4 -4
  37. package/src/cli/config.mjs +4 -2
  38. package/src/cli/initialize.mjs +8 -4
  39. package/src/cli/live.mjs +63 -0
  40. package/src/cli/main.mjs +35 -12
  41. package/src/cli/provider-loader.mjs +3 -2
  42. package/src/cloudflare-decisions.d.mts +12 -5
  43. package/src/decisions.d.mts +28 -4
  44. package/src/errors.mjs +7 -1
  45. package/src/extraction/index.d.mts +66 -0
  46. package/src/extraction/index.mjs +78 -0
  47. package/src/extraction/validation.mjs +105 -0
  48. package/src/index.d.mts +18 -4
  49. package/src/input.mjs +21 -3
  50. package/src/live/index.d.mts +73 -0
  51. package/src/live/index.mjs +170 -0
  52. package/src/live/state.mjs +125 -0
  53. package/src/live/validation.mjs +122 -0
  54. package/src/providers/cloudflare-images.mjs +3 -9
  55. package/src/providers/cloudflare-media.mjs +5 -10
  56. package/src/providers/cloudflare-preflight.mjs +4 -3
  57. package/src/providers/cloudflare-profile.mjs +10 -14
  58. package/src/providers/cloudflare-transport.mjs +12 -5
  59. package/src/providers/cloudflare-validation.mjs +3 -1
  60. package/src/providers/decisions-codec.mjs +10 -5
  61. package/src/providers/decisions-input.mjs +120 -0
  62. package/src/providers/decisions-validation.mjs +37 -69
  63. package/src/providers/jev-images.mjs +2 -8
  64. package/src/providers/jev-preflight.mjs +1 -0
  65. package/src/providers/jev-profile.mjs +2 -2
  66. package/src/providers/media-binary.mjs +0 -7
  67. package/src/providers/openai-decisions.mjs +2 -1
  68. package/src/providers/openai-errors.mjs +23 -0
  69. package/src/providers/openai-profile.mjs +2 -1
  70. package/src/response-transport.mjs +84 -17
  71. package/src/validation.mjs +26 -14
  72. package/src/version.mjs +1 -1
@@ -1,5 +1,6 @@
1
1
  import { requireCondition as check } from './errors.mjs';
2
- import { isTextWithinLimit, validateOrderedInput } from './input.mjs';
2
+ import { validateOrderedInput } from './input.mjs';
3
+ import { validateCacheOptions, validateCacheBreakpoint, validateDecisionInput } from './providers/decisions-input.mjs';
3
4
  import { validateProbabilitySum, validateResultOptions } from './probabilities.mjs';
4
5
 
5
6
  const own = (value, key) => Object.hasOwn(value, key);
@@ -81,14 +82,25 @@ function allowedKeys(value, expected, label, code = 'VALIDATION_ERROR') {
81
82
  export function validateRequest(input, defaultModel) {
82
83
  const request = cloneJson(input, 'request');
83
84
  check(isRecord(request), 'request must be an object.');
84
- allowedKeys(request, ['state', 'input', 'questions', 'model', 'safetyIdentifier'], 'request');
85
- check(own(request, 'state') !== own(request, 'input'), 'Provide exactly one of state or input.');
85
+ allowedKeys(request, ['state', 'input', 'conversation', 'questions', 'model', 'safetyIdentifier',
86
+ 'promptCacheKey', 'promptCacheOptions'], 'request');
87
+ check(['state', 'input', 'conversation'].filter(key => own(request, key)).length === 1,
88
+ 'Provide exactly one of state, input or conversation.');
86
89
  if (own(request, 'state')) check(request.state !== null && isEntry(request.state),
87
90
  'state must be text, a JSON object, or an array.');
88
- else validateOrderedInput(request.input);
91
+ else if (own(request, 'conversation')) {
92
+ check(Array.isArray(request.conversation), 'conversation must be an ordered item array.');
93
+ validateDecisionInput(request.conversation);
94
+ } else {
95
+ validateOrderedInput(request.input);
96
+ for (const part of request.input) validateCacheBreakpoint(part.promptCacheBreakpoint);
97
+ }
98
+ if (own(request, 'promptCacheKey')) check(request.promptCacheKey === null || typeof request.promptCacheKey === 'string',
99
+ 'promptCacheKey must be text or null.');
100
+ validateCacheOptions(request.promptCacheOptions);
89
101
  if (own(request, 'safetyIdentifier')) check(request.safetyIdentifier === null ||
90
- isTextWithinLimit(request.safetyIdentifier, 128),
91
- 'safetyIdentifier must be a string up to 128 characters or null.');
102
+ typeof request.safetyIdentifier === 'string',
103
+ 'safetyIdentifier must be a string or null.');
92
104
  if (own(request, 'model')) check(nonempty(request.model), 'model must be a nonempty string when provided.');
93
105
  const model = own(request, 'model') ? request.model : defaultModel;
94
106
  check(nonempty(model), 'model must be a nonempty string.');
@@ -125,14 +137,14 @@ export function validateRequest(input, defaultModel) {
125
137
  if (own(level, 'description')) check(level.description !== null && isEntry(level.description), 'Invalid level description.');
126
138
  }
127
139
  } else if (q.type === 'choice') {
128
- check(isRecord(q.criteria) && Object.keys(q.criteria).length > 0 && Object.keys(q.criteria).length <= 255,
129
- 'choice requires a criteria map with 1 to 255 options.');
140
+ check(isRecord(q.criteria) && Object.keys(q.criteria).length > 0,
141
+ 'choice requires a nonempty criteria map.');
130
142
  for (const [label, description] of Object.entries(q.criteria)) {
131
143
  check(nonempty(label) && isEntry(description), 'choice criteria require labels and text, structured JSON, or null descriptions.');
132
144
  }
133
145
  } else if (q.type === 'score') {
134
- check(Array.isArray(q.criteria) && q.criteria.length >= 2 && q.criteria.length <= 10,
135
- 'score requires 2 to 10 ordered criteria.');
146
+ check(Array.isArray(q.criteria) && q.criteria.length >= 2,
147
+ 'score requires at least two ordered criteria.');
136
148
  check(q.criteria.every(value => value !== null && isEntry(value)),
137
149
  'score descriptions must be text or structured JSON, not null.');
138
150
  } else if (own(q, 'criteria')) {
@@ -173,9 +185,9 @@ export function validateResult(input, questions, options = {}) {
173
185
  q.options.every(option => isRecord(option)), 'Typed choice requires nonempty option objects.');
174
186
  if (q.type === 'score' && own(q, 'levels')) check(Array.isArray(q.levels) && q.levels.length >= 2 &&
175
187
  q.levels.every(level => isRecord(level) && typeof level.label === 'string'), 'Labelled score requires valid levels.');
176
- if (q.type === 'choice' && !own(q, 'options')) check(isRecord(q.criteria) && Object.keys(q.criteria).length > 0 &&
177
- Object.keys(q.criteria).length <= 255, 'choice requires 1 to 255 criteria.');
178
- if (q.type === 'score' && !own(q, 'levels')) check(Array.isArray(q.criteria) && q.criteria.length >= 2 && q.criteria.length <= 10, 'score requires 2–10 levels.');
188
+ if (q.type === 'choice' && !own(q, 'options')) check(isRecord(q.criteria) && Object.keys(q.criteria).length > 0,
189
+ 'choice requires nonempty criteria.');
190
+ if (q.type === 'score' && !own(q, 'levels')) check(Array.isArray(q.criteria) && q.criteria.length >= 2, 'score requires at least two levels.');
179
191
  const answer = result.answers[id];
180
192
  check(isRecord(answer) && answer.type === q.type, 'Answer type must match its question.', 'INVALID_RESPONSE');
181
193
  allowedKeys(answer, q.type === 'box' ? ['type', 'box', 'confidence'] : q.type === 'noul' ? ['type', 'noul'] : q.type === 'choice'
@@ -202,7 +214,7 @@ export function validateResult(input, questions, options = {}) {
202
214
  if (q.type === 'choice') {
203
215
  check(typeof answer.choice === 'string' && labels.includes(answer.choice), 'choice must be a supplied option.', 'INVALID_RESPONSE');
204
216
  const selected = answer.probabilities[answer.choice];
205
- check(selected + 1e-6 >= Math.max(...Object.values(answer.probabilities)),
217
+ check(selected + 1e-6 >= Object.values(answer.probabilities).reduce((max, value) => Math.max(max, value), 0),
206
218
  'choice must select a highest-probability option.', 'INVALID_RESPONSE');
207
219
  } else {
208
220
  exactKeys(answer.legend, labels, 'legend', 'INVALID_RESPONSE');
package/src/version.mjs CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Keep in sync with package.json; scripts/check.mjs verifies the release version. */
2
- export const VERSION = '0.1.2';
2
+ export const VERSION = '0.1.3';