@corbits/system-one 0.3.1 → 0.4.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/dist/schemas.js CHANGED
@@ -88,11 +88,48 @@ export const BooleanQuestion = type({
88
88
  },
89
89
  "+": "reject",
90
90
  }).narrow((q) => nonEmpty(q.instructions));
91
+ // Locates a region in an image. Edges come back as fractions of the image.
92
+ // Callers should set the model to `jev-preview`; the client does not require it.
93
+ export const BoxQuestion = type({
94
+ id: "string",
95
+ type: "'box'",
96
+ instructions: Description,
97
+ "+": "reject",
98
+ }).narrow((q) => nonEmpty(q.instructions));
99
+ // Keyed decisions questions: choice, noul, score, or box only. Extraction-style
100
+ // helpers are not part of this contract and fail the union.
101
+ export const DecisionQuestion = type({
102
+ type: "'choice'",
103
+ instructions: "unknown",
104
+ criteria: "unknown",
105
+ "+": "reject",
106
+ })
107
+ .or(type({
108
+ type: "'noul'",
109
+ "instructions?": "unknown",
110
+ "criteria?": "unknown",
111
+ "+": "reject",
112
+ }))
113
+ .or(type({
114
+ type: "'score'",
115
+ instructions: "unknown",
116
+ criteria: "unknown",
117
+ "+": "reject",
118
+ }))
119
+ .or(type({
120
+ type: "'box'",
121
+ instructions: "unknown",
122
+ "+": "reject",
123
+ }));
124
+ export const DecisionQuestionRecord = type({
125
+ "[string]": DecisionQuestion,
126
+ }).narrow((qs) => Object.keys(qs).length >= 1);
91
127
  // The question union a caller may ask in one evaluation. `.or` chains keep
92
128
  // each branch's literal `type`, so the union still discriminates on it.
93
129
  export const Question = ChoiceQuestion.or(ScoreQuestion)
94
130
  .or(NoulQuestion)
95
- .or(BooleanQuestion);
131
+ .or(BooleanQuestion)
132
+ .or(BoxQuestion);
96
133
  // A non-empty question list with unique ids: a duplicate would make the
97
134
  // backend's per-id echo permanently ambiguous, so it is rejected at the
98
135
  // input edge (typed `SystemOneError` / `ProtocolMismatchError`) rather than
@@ -107,10 +144,13 @@ export const QuestionList = Question.array()
107
144
  // `gateway` the proxy endpoint, `custom` a caller-supplied URL. `url` only
108
145
  // exists on `custom` — a `url` on another kind would be silently ignored,
109
146
  // so the schema rejects it instead of accepting misleading config.
147
+ // `auth: "none"` exists only on `custom`: it is the explicit opt-out from
148
+ // the fail-closed key requirement. Official and gateway cannot carry it.
110
149
  export const EndpointConfig = type({
111
150
  kind: "'custom'",
112
151
  url: "string",
113
152
  "model?": "string",
153
+ "auth?": "'none'",
114
154
  "+": "reject",
115
155
  }).or(type({
116
156
  kind: "'official' | 'gateway'",
@@ -146,16 +186,22 @@ export const EvaluateInput = type({
146
186
  // `toWireRequest`, never hand-assembled by callers.
147
187
  /** One question in wire form: no id (the map key carries it). */
148
188
  export const WireQuestion = type({
149
- type: "'noul' | 'choice' | 'score'",
189
+ type: "'noul' | 'choice' | 'score' | 'box'",
150
190
  instructions: Description,
151
191
  "criteria?": "unknown",
152
192
  "+": "reject",
153
193
  });
154
- /** The exact POST body: model + state + id-keyed questions. */
194
+ const WireAsset = type({
195
+ type: "'image'",
196
+ part: "string",
197
+ "+": "reject",
198
+ });
199
+ /** The exact POST body: model + state + id-keyed questions, plus image parts. */
155
200
  export const WireRequest = type({
156
201
  model: "string",
157
202
  state: State,
158
203
  questions: { "[string]": WireQuestion },
204
+ "assets?": { "[string]": WireAsset },
159
205
  "+": "reject",
160
206
  }).narrow((body) => Object.keys(body.questions).length >= 1);
161
207
  /**
@@ -169,8 +215,9 @@ export function toWireQuestions(questions) {
169
215
  type: question.type === "boolean" ? "noul" : question.type,
170
216
  instructions: question.instructions,
171
217
  };
172
- if (question.criteria !== undefined)
218
+ if ("criteria" in question && question.criteria !== undefined) {
173
219
  mapped.criteria = question.criteria;
220
+ }
174
221
  wire[question.id] = mapped;
175
222
  }
176
223
  return wire;
@@ -193,14 +240,36 @@ export const APPROXIMATE_SUM_TOLERANCE = 0.02;
193
240
  // `type` must carry. No `"+": "reject"`: the backend may attach per-answer
194
241
  // metadata that `toDecision` strips.
195
242
  export const WireAnswer = type({
196
- type: "'noul' | 'choice' | 'score'",
243
+ type: "'noul' | 'choice' | 'score' | 'box'",
197
244
  "choice?": "string",
198
245
  "score?": "number",
199
246
  "noul?": Confidence,
200
247
  "probabilities?": ProbabilityMap,
201
- "confidence?": Confidence,
248
+ "confidence?": Confidence.or("null"),
202
249
  "legend?": { "[string]": "string" },
250
+ "box?": {
251
+ top: "number",
252
+ bottom: "number",
253
+ left: "number",
254
+ right: "number",
255
+ },
203
256
  });
257
+ /** Box edges are fractions of the image. Shared by `evaluate` and the adapter. */
258
+ export function boxEdgesOutOfRange(id, box) {
259
+ for (const edge of ["top", "bottom", "left", "right"]) {
260
+ const value = box[edge];
261
+ if (!Number.isFinite(value) || value < 0 || value > 1) {
262
+ return `answer "${id}" box.${edge} ${String(value)} outside [0, 1]`;
263
+ }
264
+ }
265
+ if (box.top >= box.bottom) {
266
+ return `answer "${id}" box.top ${String(box.top)} is not below box.bottom ${String(box.bottom)}`;
267
+ }
268
+ if (box.left >= box.right) {
269
+ return `answer "${id}" box.left ${String(box.left)} is not left of box.right ${String(box.right)}`;
270
+ }
271
+ return undefined;
272
+ }
204
273
  // Token usage reported on the response envelope.
205
274
  export const WireUsage = type({
206
275
  input_tokens: "number",
@@ -241,6 +310,19 @@ export const Decision = type({
241
310
  probabilities: ProbabilityMap,
242
311
  confidence: Confidence,
243
312
  "+": "reject",
313
+ }))
314
+ .or(type({
315
+ id: "string",
316
+ type: "'box'",
317
+ box: {
318
+ top: "number",
319
+ bottom: "number",
320
+ left: "number",
321
+ right: "number",
322
+ "+": "reject",
323
+ },
324
+ confidence: Confidence.or("null"),
325
+ "+": "reject",
244
326
  }));
245
327
  /**
246
328
  * Builds a caller-facing decision from a wire answer: the kind's contract
@@ -262,6 +344,14 @@ export function toDecision(id, answer) {
262
344
  confidence: answer.confidence,
263
345
  };
264
346
  }
347
+ else if (answer.type === "box") {
348
+ candidate = {
349
+ id,
350
+ type: "box",
351
+ box: answer.box,
352
+ confidence: answer.confidence ?? null,
353
+ };
354
+ }
265
355
  else {
266
356
  candidate = {
267
357
  id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@corbits/system-one",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Typed-decision evaluation client for System One (Jev-class) models: arktype question/decision schemas, endpoint configuration with gateway override, and timeout/fallback semantics.",
5
5
  "keywords": [
6
6
  "corbits",
@@ -29,8 +29,19 @@
29
29
  "sideEffects": false,
30
30
  "exports": {
31
31
  ".": {
32
+ "corbits-system-one-src": "./src/index.ts",
32
33
  "types": "./dist/index.d.ts",
33
34
  "default": "./dist/index.js"
35
+ },
36
+ "./decisions": {
37
+ "corbits-system-one-src": "./src/decisions.ts",
38
+ "types": "./dist/decisions.d.ts",
39
+ "default": "./dist/decisions.js"
40
+ },
41
+ "./extraction": {
42
+ "corbits-system-one-src": "./src/extraction.ts",
43
+ "types": "./dist/extraction.d.ts",
44
+ "default": "./dist/extraction.js"
34
45
  }
35
46
  },
36
47
  "publishConfig": {
@@ -51,17 +62,17 @@
51
62
  "arktype": "2.2.3"
52
63
  },
53
64
  "devDependencies": {
54
- "@intx/inference": "0.4.0",
55
- "@intx/inference-testing": "0.4.0",
56
- "@intx/types": "0.4.0",
65
+ "@intx/inference": "0.5.0",
66
+ "@intx/inference-testing": "0.5.0",
67
+ "@intx/types": "0.5.0",
57
68
  "@types/bun": "1.3.14",
58
69
  "oxfmt": "0.70.0",
59
70
  "oxlint": "1.85.0",
60
71
  "typescript": "6.0.3"
61
72
  },
62
73
  "peerDependencies": {
63
- "@intx/inference": "^0.4.0",
64
- "@intx/types": "^0.4.0"
74
+ "@intx/inference": "^0.5.0",
75
+ "@intx/types": "^0.5.0"
65
76
  },
66
77
  "engines": {
67
78
  "bun": ">=1.2.0",