zorya-classifier 0.2.0 → 0.2.2

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 (140) hide show
  1. package/README.md +24 -8
  2. package/dist/app/classifier-config.d.ts +30 -0
  3. package/dist/app/classifier-config.js +46 -0
  4. package/dist/app/classifier-contracts.d.ts +32 -0
  5. package/dist/app/classifier-contracts.js +1 -0
  6. package/dist/app/classifier-representation.d.ts +5 -0
  7. package/dist/app/classifier-representation.js +33 -0
  8. package/dist/app/classifier-runtime.d.ts +7 -0
  9. package/dist/app/classifier-runtime.js +71 -0
  10. package/dist/app/classifier-spatial.d.ts +31 -0
  11. package/dist/app/classifier-spatial.js +78 -0
  12. package/dist/app/cli-arguments.d.ts +2 -2
  13. package/dist/app/create-classifier.d.ts +4 -29
  14. package/dist/app/create-classifier.js +37 -237
  15. package/dist/cli.js +1 -1
  16. package/dist/entities/classification.d.ts +98 -0
  17. package/dist/entities/classification.js +28 -0
  18. package/dist/entities/contracts.d.ts +6 -471
  19. package/dist/entities/contracts.js +6 -97
  20. package/dist/entities/decision.d.ts +211 -0
  21. package/dist/entities/decision.js +33 -0
  22. package/dist/entities/geometry.d.ts +37 -0
  23. package/dist/entities/geometry.js +26 -0
  24. package/dist/entities/image.d.ts +39 -0
  25. package/dist/entities/image.js +15 -0
  26. package/dist/entities/localization.d.ts +125 -0
  27. package/dist/entities/localization.js +13 -0
  28. package/dist/entities/object-proposals.d.ts +4 -1
  29. package/dist/entities/segmentation-metadata.d.ts +70 -0
  30. package/dist/entities/segmentation-metadata.js +21 -1
  31. package/dist/entities/segmentation-verification.d.ts +1 -0
  32. package/dist/entities/segmentation.d.ts +6 -1
  33. package/dist/entities/validation.d.ts +2 -0
  34. package/dist/entities/validation.js +9 -0
  35. package/dist/entities/whole-object.d.ts +4 -0
  36. package/dist/features/choice-beam-layout.js +2 -7
  37. package/dist/features/proposals/presentation.d.ts +21 -0
  38. package/dist/features/proposals/presentation.js +122 -0
  39. package/dist/features/proposals/validation.d.ts +138 -0
  40. package/dist/features/proposals/validation.js +125 -0
  41. package/dist/features/segmentation/box-check.js +10 -23
  42. package/dist/features/segmentation/mask.js +2 -7
  43. package/dist/features/segmentation/region-result.d.ts +18 -0
  44. package/dist/features/segmentation/region-result.js +171 -0
  45. package/dist/features/segmentation/source-mask.d.ts +6 -0
  46. package/dist/features/segmentation/source-mask.js +32 -0
  47. package/dist/features/segmentation/validation.d.ts +9 -0
  48. package/dist/features/segmentation/validation.js +94 -0
  49. package/dist/infrastructure/decision-transport/errors.d.ts +8 -0
  50. package/dist/infrastructure/decision-transport/errors.js +34 -0
  51. package/dist/infrastructure/decision-transport/images.d.ts +1 -0
  52. package/dist/infrastructure/decision-transport/images.js +52 -0
  53. package/dist/infrastructure/{openrouter-common.d.ts → decision-transport/request.d.ts} +6 -15
  54. package/dist/infrastructure/decision-transport/request.js +162 -0
  55. package/dist/infrastructure/decision-transport/response.d.ts +1 -0
  56. package/dist/infrastructure/decision-transport/response.js +72 -0
  57. package/dist/infrastructure/decision-transport/route-identity.d.ts +11 -0
  58. package/dist/infrastructure/decision-transport/route-identity.js +23 -0
  59. package/dist/infrastructure/openrouter-vision.js +1 -1
  60. package/dist/infrastructure/openrouter.js +7 -19
  61. package/dist/infrastructure/raster.js +3 -8
  62. package/dist/infrastructure/segmentation/grabcut/index.d.ts +5 -1
  63. package/dist/infrastructure/segmentation/grabcut/index.js +6 -5
  64. package/dist/infrastructure/segmentation/grabcut/masks.d.ts +49 -0
  65. package/dist/infrastructure/segmentation/grabcut/masks.js +89 -0
  66. package/dist/infrastructure/segmentation/grabcut/model-asset.d.ts +4 -0
  67. package/dist/infrastructure/segmentation/grabcut/model-asset.js +4 -0
  68. package/dist/infrastructure/segmentation/grabcut/runtime.d.ts +70 -0
  69. package/dist/infrastructure/segmentation/grabcut/runtime.js +96 -0
  70. package/dist/infrastructure/segmentation/grabcut/worker-inference.d.ts +4 -43
  71. package/dist/infrastructure/segmentation/grabcut/worker-inference.js +4 -187
  72. package/dist/infrastructure/segmentation/mobile-sam/index.d.ts +3 -58
  73. package/dist/infrastructure/segmentation/mobile-sam/index.js +2 -472
  74. package/dist/infrastructure/segmentation/mobile-sam/input.d.ts +46 -0
  75. package/dist/infrastructure/segmentation/mobile-sam/input.js +83 -0
  76. package/dist/infrastructure/segmentation/mobile-sam/model-assets.d.ts +8 -0
  77. package/dist/infrastructure/segmentation/mobile-sam/model-assets.js +8 -0
  78. package/dist/infrastructure/segmentation/mobile-sam/object-proposals.d.ts +7 -0
  79. package/dist/infrastructure/segmentation/mobile-sam/object-proposals.js +124 -0
  80. package/dist/infrastructure/segmentation/mobile-sam/options.d.ts +43 -0
  81. package/dist/infrastructure/segmentation/mobile-sam/options.js +19 -0
  82. package/dist/infrastructure/segmentation/mobile-sam/proposal-masks.d.ts +26 -0
  83. package/dist/infrastructure/segmentation/mobile-sam/proposal-masks.js +71 -0
  84. package/dist/infrastructure/segmentation/mobile-sam/segmentation-masks.d.ts +112 -0
  85. package/dist/infrastructure/segmentation/mobile-sam/segmentation-masks.js +230 -0
  86. package/dist/infrastructure/segmentation/mobile-sam/segmenter.d.ts +22 -0
  87. package/dist/infrastructure/segmentation/mobile-sam/segmenter.js +268 -0
  88. package/dist/infrastructure/segmentation/mobile-sam/worker-expand.d.ts +7 -0
  89. package/dist/infrastructure/segmentation/mobile-sam/worker-expand.js +188 -0
  90. package/dist/infrastructure/segmentation/mobile-sam/worker-inference.d.ts +6 -149
  91. package/dist/infrastructure/segmentation/mobile-sam/worker-inference.js +6 -845
  92. package/dist/infrastructure/segmentation/mobile-sam/worker-models.d.ts +14 -0
  93. package/dist/infrastructure/segmentation/mobile-sam/worker-models.js +72 -0
  94. package/dist/infrastructure/segmentation/mobile-sam/worker-proposals.d.ts +7 -0
  95. package/dist/infrastructure/segmentation/mobile-sam/worker-proposals.js +159 -0
  96. package/dist/infrastructure/segmentation/mobile-sam/worker-segment.d.ts +4 -0
  97. package/dist/infrastructure/segmentation/mobile-sam/worker-segment.js +126 -0
  98. package/dist/infrastructure/segmentation/native-protocol.d.ts +59 -34
  99. package/dist/infrastructure/segmentation/native-protocol.js +26 -16
  100. package/dist/infrastructure/segmentation/proposal-presentation-input.d.ts +20 -0
  101. package/dist/infrastructure/segmentation/proposal-presentation-input.js +107 -0
  102. package/dist/infrastructure/segmentation/proposal-presentation-pixels.d.ts +8 -0
  103. package/dist/infrastructure/segmentation/proposal-presentation-pixels.js +93 -0
  104. package/dist/infrastructure/segmentation/proposal-presentation.js +10 -201
  105. package/dist/infrastructure/vision-chat/options.d.ts +62 -0
  106. package/dist/infrastructure/vision-chat/options.js +46 -0
  107. package/dist/infrastructure/vision-chat/request.d.ts +23 -0
  108. package/dist/infrastructure/vision-chat/request.js +109 -0
  109. package/dist/infrastructure/vision-chat/response.d.ts +3 -0
  110. package/dist/infrastructure/vision-chat/response.js +105 -0
  111. package/dist/infrastructure/vision-chat.d.ts +4 -61
  112. package/dist/infrastructure/vision-chat.js +8 -271
  113. package/dist/openai-compatible.js +1 -1
  114. package/dist/processes/extract-from-region.js +11 -216
  115. package/dist/processes/extract.d.ts +2 -8
  116. package/dist/processes/extract.js +12 -128
  117. package/dist/processes/proposal-location.js +5 -251
  118. package/docs/0.2.0-plan.md +3 -3
  119. package/docs/README.ru.md +42 -23
  120. package/docs/acceptance.md +5 -5
  121. package/docs/agents.ru.md +95 -0
  122. package/docs/api.md +4 -2
  123. package/docs/architecture.md +33 -5
  124. package/docs/architecture.ru.md +146 -0
  125. package/docs/benchmarks.md +4 -2
  126. package/docs/development.ru.md +141 -0
  127. package/docs/operations.md +4 -4
  128. package/docs/provenance.json +5 -1
  129. package/docs/publishing.md +8 -0
  130. package/docs/refactoring-plan.ru.md +183 -0
  131. package/docs/v02-usage.md +8 -6
  132. package/docs/vision-results.md +2 -0
  133. package/package.json +5 -3
  134. package/dist/infrastructure/boundary-view.d.ts +0 -2
  135. package/dist/infrastructure/boundary-view.js +0 -263
  136. package/dist/infrastructure/openrouter-common.js +0 -317
  137. package/dist/infrastructure/text-representation.d.ts +0 -23
  138. package/dist/infrastructure/text-representation.js +0 -226
  139. package/docs/evidence/v02/categorical-heldout-v4/README.md +0 -35
  140. package/docs/evidence/v02/proposal-resource-soak-261dc23.md +0 -15
@@ -1,77 +1,26 @@
1
- import { z } from 'zod';
2
1
  import { classifySchema, locateSchema, parse } from '../entities/contracts.js';
3
2
  import { ClassifierError } from '../entities/errors.js';
4
3
  import { createSharpRaster } from '../infrastructure/raster.js';
5
4
  import { classifyInput } from '../processes/classify.js';
6
5
  import { locateInput } from '../processes/locate.js';
7
- import { OperationGate } from '../shared/gate.js';
8
- import { OperationScope } from '../shared/scope.js';
9
- import { InputBudget } from '../shared/input-budget.js';
10
6
  import { wholeObjectSchema } from '../entities/whole-object.js';
11
- import { locateWholeObjectInput } from '../processes/whole-object.js';
12
- import { preparedTextSchema, representationLimits } from '../entities/representation.js';
13
7
  import { extractSchema } from '../entities/segmentation.js';
14
- import { extractObject } from '../processes/extract.js';
15
- import { extractFromRegion } from '../processes/extract-from-region.js';
16
8
  import { createSharpCutoutRaster } from '../infrastructure/segmentation/cutout-raster.js';
17
- import { locateFromProposals } from '../processes/proposal-location.js';
18
9
  import { createSharpObjectProposalPresentation } from '../infrastructure/segmentation/proposal-presentation.js';
19
10
  import { createSegmentationBoxCheck } from '../features/segmentation/box-check.js';
20
11
  import { createSharpSegmentationBoxPresentation } from '../infrastructure/segmentation/box-presentation.js';
21
- const configSchema = z.object({
22
- timeoutMs: z.number().int().min(1).max(120000).default(45000),
23
- concurrency: z.number().int().min(1).max(4).default(2),
24
- maxQueue: z.number().int().min(0).max(32).default(16),
25
- maxInputBytes: z
26
- .number()
27
- .int()
28
- .min(1)
29
- .max(256 * 1024 * 1024)
30
- .default(64 * 1024 * 1024),
31
- });
32
- function signalOf(value) {
33
- if (value === undefined)
34
- return undefined;
35
- if (!(value instanceof AbortSignal))
36
- throw new ClassifierError('INVALID_INPUT', 'signal must be an AbortSignal');
37
- return value;
38
- }
12
+ import { validateClassifierOptions } from './classifier-config.js';
13
+ import { createClassifierRepresentation } from './classifier-representation.js';
14
+ import { createClassifierRuntime, parseOperationSignal } from './classifier-runtime.js';
15
+ import { createClassifierSpatial } from './classifier-spatial.js';
39
16
  export function createClassifier(options) {
40
17
  return composeClassifier(options);
41
18
  }
42
- /** Internal benchmark composition. No research codec is exported by the npm entry points. */
43
- export function createResearchClassifier(options, experimental, boundaryView, intervalPolicy) {
44
- return composeClassifier(options, experimental, boundaryView, intervalPolicy);
45
- }
46
- function composeClassifier(options, experimental, boundaryView, intervalPolicy) {
47
- if (!options || typeof options.decision?.decide !== 'function')
48
- throw new ClassifierError('INVALID_INPUT', 'A DecisionPort is required');
49
- const segmenter = options.segmentation;
50
- const objectProposals = options.objectProposals;
51
- if (segmenter !== undefined &&
52
- (!segmenter ||
53
- typeof segmenter.segment !== 'function' ||
54
- (segmenter.expand !== undefined && typeof segmenter.expand !== 'function') ||
55
- typeof segmenter.settle !== 'function' ||
56
- typeof segmenter.dispose !== 'function'))
57
- throw new ClassifierError('INVALID_INPUT', 'segmentation must implement SegmentationPort');
58
- if (options.segmentationBoxCheck !== undefined &&
59
- (!options.segmentationBoxCheck || typeof options.segmentationBoxCheck.check !== 'function'))
60
- throw new ClassifierError('INVALID_INPUT', 'segmentationBoxCheck must implement its check port');
61
- if (objectProposals !== undefined &&
62
- (!objectProposals ||
63
- typeof objectProposals.propose !== 'function' ||
64
- typeof objectProposals.settle !== 'function' ||
65
- typeof objectProposals.dispose !== 'function'))
66
- throw new ClassifierError('INVALID_INPUT', 'objectProposals must implement ObjectProposalPort');
67
- const config = parse(configSchema, {
68
- timeoutMs: options.timeoutMs,
69
- concurrency: options.concurrency,
70
- maxQueue: options.maxQueue,
71
- maxInputBytes: options.maxInputBytes,
72
- });
73
- const gate = new OperationGate(config.concurrency, config.maxQueue);
74
- const inputBudget = new InputBudget(config.maxInputBytes);
19
+ /** Internal composition seam for benchmark helpers; not re-exported from public entry points. */
20
+ export function composeClassifier(options, experimental, boundaryView, intervalPolicy) {
21
+ const { config, ports } = validateClassifierOptions(options);
22
+ const { segmenter, objectProposals } = ports;
23
+ const runtime = createClassifierRuntime(config);
75
24
  const raster = createSharpRaster();
76
25
  const cutoutRaster = createSharpCutoutRaster();
77
26
  const boxCheck = segmenter
@@ -91,146 +40,47 @@ function composeClassifier(options, experimental, boundaryView, intervalPolicy)
91
40
  presentation: createSharpObjectProposalPresentation(),
92
41
  }
93
42
  : undefined;
94
- const owned = new Map();
95
- let disposed = false;
96
- const textRepresentation = experimental
97
- ? {
98
- async prepare(input) {
99
- const result = parse(preparedTextSchema, await experimental.prepare(input), 'INVALID_RESPONSE');
100
- const bytes = result.state.reduce((sum, part) => sum + Buffer.byteLength(part, 'utf8'), 0);
101
- if (bytes > representationLimits.totalBytes || bytes !== result.metadata.bytes)
102
- throw new ClassifierError('INVALID_RESPONSE', 'Representation byte accounting is inconsistent');
103
- return {
104
- state: result.state,
105
- metadata: {
106
- ...result.metadata,
107
- views: result.metadata.views.map(({ content, ...view }) => ({
108
- ...view,
109
- ...(content === undefined ? {} : { content }),
110
- })),
111
- },
112
- };
113
- },
114
- }
115
- : undefined;
116
- function representationFor(value) {
117
- if (value?.type !== 'experimental-text')
118
- return undefined;
119
- if (!textRepresentation)
120
- throw new ClassifierError('UNSUPPORTED', 'No text-image profile has passed release qualification yet');
121
- return textRepresentation;
122
- }
123
- async function locateWhole(input, signal, representation) {
124
- if (segmenter) {
125
- const result = await extractRegion(input, signal, representation);
126
- if (result.status === 'extracted')
127
- return result.location;
128
- if (result.status === 'unknown' && result.reason === 'segmentation-uncertain')
129
- return {
130
- status: 'unknown',
131
- reason: 'segmentation-uncertain',
132
- metadata: 'metadata' in result ? result.metadata : result.location.metadata,
133
- ...(result.verification ? { verification: result.verification } : {}),
134
- };
135
- return result;
136
- }
137
- if (proposalDependencies) {
138
- if (representation)
139
- throw new ClassifierError('UNSUPPORTED', 'Mask proposal selection requires vision input');
140
- return (await locateFromProposals(input, proposalDependencies, signal)).location;
141
- }
142
- return locateWholeObjectInput(input, options.decision, raster, signal, representation, boundaryView, intervalPolicy);
143
- }
144
- function extractRegion(input, signal, representation) {
145
- if (!segmenter || !boxCheck)
146
- throw new ClassifierError('UNSUPPORTED', 'Region extraction requires a SegmentationPort');
147
- if (representation)
148
- throw new ClassifierError('UNSUPPORTED', 'Checked segmentation requires vision input');
149
- return extractFromRegion({ ...input, signal }, {
150
- raster,
151
- image: cutoutRaster,
152
- segmenter,
153
- boxCheck,
154
- locateRegion: (source, { signal: operationSignal, ...request }) => locateInput({ ...request, image: source.bytes }, options.decision, raster, operationSignal, undefined, representation),
155
- });
156
- }
157
- async function run(signal, task) {
158
- if (disposed)
159
- throw new ClassifierError('DISPOSED', 'Classifier has been disposed');
160
- const scope = new OperationScope(config.timeoutMs, signal);
161
- const job = (async () => {
162
- let acquired = false;
163
- try {
164
- await gate.acquire(scope.signal);
165
- acquired = true;
166
- scope.guard();
167
- const result = await task(scope.signal);
168
- scope.guard();
169
- return result;
170
- }
171
- catch (error) {
172
- scope.guard();
173
- throw error;
174
- }
175
- finally {
176
- if (acquired)
177
- gate.release();
178
- scope.dispose();
179
- owned.delete(scope);
180
- }
181
- })();
182
- owned.set(scope, job);
183
- return job;
184
- }
43
+ const { representationFor } = createClassifierRepresentation(experimental);
44
+ const spatial = createClassifierSpatial({
45
+ getDecision: () => options.decision,
46
+ raster,
47
+ cutoutRaster,
48
+ segmenter,
49
+ boxCheck,
50
+ proposalDependencies,
51
+ boundaryView,
52
+ intervalPolicy,
53
+ });
185
54
  async function locate(input) {
186
- if (disposed)
187
- throw new ClassifierError('DISPOSED', 'Classifier has been disposed');
188
- gate.assertAvailable();
55
+ runtime.assertAcceptingOperations();
189
56
  if (!input || typeof input !== 'object')
190
57
  throw new ClassifierError('INVALID_INPUT', 'Expected locate input object');
191
58
  const { signal, ...rest } = input;
192
- const parent = signalOf(signal);
59
+ const parent = parseOperationSignal(signal);
193
60
  if (rest.mode === 'whole-object') {
194
61
  const parsed = parse(wholeObjectSchema, rest);
195
62
  const representation = representationFor(parsed.representation);
196
- const release = inputBudget.reserve(parsed.image.byteLength);
197
- try {
198
- const snapshot = { ...parsed, image: new Uint8Array(parsed.image) };
199
- return await run(parent, (s) => locateWhole(snapshot, s, representation));
200
- }
201
- finally {
202
- release();
203
- }
63
+ return runtime.withOwnedInput(parsed.image.byteLength, parent, () => ({ ...parsed, image: new Uint8Array(parsed.image) }), (snapshot, s) => spatial.locateWhole(snapshot, s, representation));
204
64
  }
205
65
  const parsed = parse(locateSchema, rest);
206
66
  const representation = representationFor(parsed.representation);
207
- const release = inputBudget.reserve(parsed.image.byteLength);
208
- try {
209
- const snapshot = { ...parsed, image: new Uint8Array(parsed.image) };
210
- return await run(parent, (s) => locateInput(snapshot, options.decision, raster, s, options.refinement, representation));
211
- }
212
- finally {
213
- release();
214
- }
67
+ return runtime.withOwnedInput(parsed.image.byteLength, parent, () => ({ ...parsed, image: new Uint8Array(parsed.image) }), (snapshot, s) => locateInput(snapshot, options.decision, raster, s, options.refinement, representation));
215
68
  }
216
69
  return {
217
70
  async classify(input) {
218
- if (disposed)
219
- throw new ClassifierError('DISPOSED', 'Classifier has been disposed');
220
- gate.assertAvailable();
71
+ runtime.assertAcceptingOperations();
221
72
  if (!input || typeof input !== 'object')
222
73
  throw new ClassifierError('INVALID_INPUT', 'Expected classify input object');
223
74
  const { signal, ...rest } = input;
224
- const parent = signalOf(signal);
75
+ const parent = parseOperationSignal(signal);
225
76
  const parsed = parse(classifySchema, rest);
226
77
  const hasImage = typeof parsed.input !== 'string' && parsed.input.image !== undefined;
227
78
  const representation = hasImage ? representationFor(parsed.representation) : undefined;
228
79
  const byteCount = typeof parsed.input === 'string'
229
80
  ? Buffer.byteLength(parsed.input)
230
81
  : (parsed.input.image?.byteLength ?? 0) + Buffer.byteLength(parsed.input.text ?? '');
231
- const release = inputBudget.reserve(byteCount);
232
- try {
233
- const media = typeof parsed.input === 'string'
82
+ return runtime.withOwnedInput(byteCount, parent, () => {
83
+ return typeof parsed.input === 'string'
234
84
  ? parsed.input
235
85
  : {
236
86
  ...parsed.input,
@@ -238,75 +88,25 @@ function composeClassifier(options, experimental, boundaryView, intervalPolicy)
238
88
  ? {}
239
89
  : { image: new Uint8Array(parsed.input.image) }),
240
90
  };
241
- return await run(parent, (s) => classifyInput({ ...parsed, input: media }, options.decision, raster, s, representation));
242
- }
243
- finally {
244
- release();
245
- }
91
+ }, (media, s) => classifyInput({ ...parsed, input: media }, options.decision, raster, s, representation));
246
92
  },
247
93
  locate,
248
94
  async extract(input) {
249
- if (disposed)
250
- throw new ClassifierError('DISPOSED', 'Classifier has been disposed');
251
- gate.assertAvailable();
95
+ runtime.assertAcceptingOperations();
252
96
  if (!input || typeof input !== 'object')
253
97
  throw new ClassifierError('INVALID_INPUT', 'Expected extract input object');
254
98
  const { signal, ...rest } = input;
255
- const parent = signalOf(signal);
99
+ const parent = parseOperationSignal(signal);
256
100
  const parsed = parse(extractSchema, rest);
257
101
  if (!segmenter && !proposalDependencies)
258
102
  throw new ClassifierError('UNSUPPORTED', 'extract requires an explicit SegmentationPort or ObjectProposalPort');
259
103
  const representation = representationFor(parsed.representation);
260
- const release = inputBudget.reserve(parsed.image.byteLength);
261
- try {
262
- const snapshot = { ...parsed, image: new Uint8Array(parsed.image) };
263
- return await run(parent, (s) => {
264
- if (segmenter)
265
- return extractRegion(snapshot, s, representation);
266
- let prepared;
267
- // Per-operation ownership: no global selected-mask cache or second native inference.
268
- const selectedMask = {
269
- async segment() {
270
- if (!prepared?.mask || !prepared.segmentation)
271
- throw new ClassifierError('INVALID_RESPONSE', 'Proposal selection omitted its mask');
272
- return {
273
- status: 'segmented',
274
- mask: prepared.mask,
275
- metadata: prepared.segmentation,
276
- };
277
- },
278
- async settle() { },
279
- async dispose() { },
280
- };
281
- return extractObject({ ...snapshot, mode: 'whole-object', signal: s }, {
282
- raster,
283
- image: cutoutRaster,
284
- segmenter: selectedMask,
285
- locateWhole: async (source, { signal: requestSignal, ...request }) => {
286
- const wholeInput = {
287
- ...request,
288
- mode: 'whole-object',
289
- image: source.bytes,
290
- };
291
- if (!proposalDependencies)
292
- throw new ClassifierError('UNSUPPORTED', 'ObjectProposalPort is required');
293
- if (representation)
294
- throw new ClassifierError('UNSUPPORTED', 'Mask proposal selection requires vision input');
295
- prepared = await locateFromProposals(wholeInput, proposalDependencies, requestSignal);
296
- return prepared.location;
297
- },
298
- });
299
- });
300
- }
301
- finally {
302
- release();
303
- }
304
- },
305
- async dispose() {
306
- disposed = true;
307
- for (const scope of owned.keys())
308
- scope.abort();
309
- await Promise.allSettled(owned.values());
104
+ return runtime.withOwnedInput(parsed.image.byteLength, parent, () => ({ ...parsed, image: new Uint8Array(parsed.image) }), (snapshot, s) => {
105
+ if (segmenter)
106
+ return spatial.extractRegion(snapshot, s, representation);
107
+ return spatial.extractWithProposals(snapshot, s, representation);
108
+ });
310
109
  },
110
+ dispose: () => runtime.dispose(),
311
111
  };
312
112
  }
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import { createMobileSamSegmenter, createGrabCutSegmenter } from './segmentation
8
8
  async function main() {
9
9
  const args = parseCliArguments(process.argv.slice(2));
10
10
  if (args.command === 'help') {
11
- console.log('zorya-classifier classify-text <text> <classes.json>\nzorya-classifier classify-image <image> <classes.json>\nzorya-classifier locate <image> <target> [--mode region|whole-object]\nzorya-classifier extract <image> <target> --output <prefix> --backend mobile-sam --encoder <path> --decoder <path> [--provider cpu|dml]\nzorya-classifier extract <image> <target> --output <prefix> --backend grabcut --opencv <path>\nSet ZORYA_MODEL. Default ZORYA_MODE=decisions; use chat for ordinary vision models/custom chat endpoints. OpenRouter: OPENROUTER_API_KEY + ZORYA_PROVIDER. Direct: ZORYA_ENDPOINT + optional ZORYA_API_KEY. Optional ZORYA_REASONING_EFFORT and ZORYA_RESPONSE_FORMAT. One classify request; up to three region or six whole-object requests. Extraction additionally runs the explicit local backend. Output files are never overwritten; native assets are never downloaded automatically.');
11
+ console.log('zorya-classifier classify-text <text> <classes.json>\nzorya-classifier classify-image <image> <classes.json>\nzorya-classifier locate <image> <target> [--mode region|whole-object] (experimental, unqualified spatial search)\nzorya-classifier extract <image> <target> --output <prefix> --backend mobile-sam --encoder <path> --decoder <path> [--provider cpu|dml] (experimental extraction)\nzorya-classifier extract <image> <target> --output <prefix> --backend grabcut --opencv <path> (experimental extraction)\nSet ZORYA_MODEL. Default ZORYA_MODE=decisions; use chat for ordinary vision models/custom chat endpoints. OpenRouter: OPENROUTER_API_KEY + ZORYA_PROVIDER. Direct: ZORYA_ENDPOINT + optional ZORYA_API_KEY. Optional ZORYA_REASONING_EFFORT and ZORYA_RESPONSE_FORMAT. classify is supported; locate/extract/object backends are experimental and not qualified for general use. One classify request; up to three region or six whole-object requests. Extraction additionally runs the explicit local backend. Output files are never overwritten; native assets are never downloaded automatically.');
12
12
  return;
13
13
  }
14
14
  const { command, value, target } = args;
@@ -0,0 +1,98 @@
1
+ import { z } from 'zod';
2
+ export declare const classSchema: z.ZodObject<{
3
+ id: z.ZodString;
4
+ description: z.ZodString;
5
+ }, "strict", z.ZodTypeAny, {
6
+ id: string;
7
+ description: string;
8
+ }, {
9
+ id: string;
10
+ description: string;
11
+ }>;
12
+ export type ClassDefinition = z.infer<typeof classSchema>;
13
+ export declare const classifySchema: z.ZodObject<{
14
+ input: z.ZodUnion<[z.ZodString, z.ZodEffects<z.ZodObject<{
15
+ text: z.ZodOptional<z.ZodString>;
16
+ image: z.ZodOptional<z.ZodEffects<z.ZodType<Uint8Array<ArrayBufferLike>, z.ZodTypeDef, Uint8Array<ArrayBufferLike>>, Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>>>;
17
+ }, "strict", z.ZodTypeAny, {
18
+ text?: string | undefined;
19
+ image?: Uint8Array<ArrayBufferLike> | undefined;
20
+ }, {
21
+ text?: string | undefined;
22
+ image?: Uint8Array<ArrayBufferLike> | undefined;
23
+ }>, {
24
+ text?: string | undefined;
25
+ image?: Uint8Array<ArrayBufferLike> | undefined;
26
+ }, {
27
+ text?: string | undefined;
28
+ image?: Uint8Array<ArrayBufferLike> | undefined;
29
+ }>]>;
30
+ classes: z.ZodEffects<z.ZodArray<z.ZodObject<{
31
+ id: z.ZodString;
32
+ description: z.ZodString;
33
+ }, "strict", z.ZodTypeAny, {
34
+ id: string;
35
+ description: string;
36
+ }, {
37
+ id: string;
38
+ description: string;
39
+ }>, "many">, {
40
+ id: string;
41
+ description: string;
42
+ }[], {
43
+ id: string;
44
+ description: string;
45
+ }[]>;
46
+ instructions: z.ZodOptional<z.ZodString>;
47
+ representation: z.ZodOptional<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
48
+ type: z.ZodLiteral<"vision">;
49
+ }, "strict", z.ZodTypeAny, {
50
+ type: "vision";
51
+ }, {
52
+ type: "vision";
53
+ }>, z.ZodObject<{
54
+ type: z.ZodLiteral<"experimental-text">;
55
+ profile: z.ZodLiteral<"text-image-v1">;
56
+ }, "strict", z.ZodTypeAny, {
57
+ type: "experimental-text";
58
+ profile: "text-image-v1";
59
+ }, {
60
+ type: "experimental-text";
61
+ profile: "text-image-v1";
62
+ }>]>>;
63
+ }, "strict", z.ZodTypeAny, {
64
+ input: string | {
65
+ text?: string | undefined;
66
+ image?: Uint8Array<ArrayBufferLike> | undefined;
67
+ };
68
+ classes: {
69
+ id: string;
70
+ description: string;
71
+ }[];
72
+ instructions?: string | undefined;
73
+ representation?: {
74
+ type: "vision";
75
+ } | {
76
+ type: "experimental-text";
77
+ profile: "text-image-v1";
78
+ } | undefined;
79
+ }, {
80
+ input: string | {
81
+ text?: string | undefined;
82
+ image?: Uint8Array<ArrayBufferLike> | undefined;
83
+ };
84
+ classes: {
85
+ id: string;
86
+ description: string;
87
+ }[];
88
+ instructions?: string | undefined;
89
+ representation?: {
90
+ type: "vision";
91
+ } | {
92
+ type: "experimental-text";
93
+ profile: "text-image-v1";
94
+ } | undefined;
95
+ }>;
96
+ export type ClassifyInput = z.input<typeof classifySchema> & {
97
+ signal?: AbortSignal;
98
+ };
@@ -0,0 +1,28 @@
1
+ import { z } from 'zod';
2
+ import { imageSchema, limits, representationSchema } from './image.js';
3
+ export const classSchema = z
4
+ .object({
5
+ id: z
6
+ .string()
7
+ .min(1)
8
+ .max(128)
9
+ .regex(/^[a-zA-Z][a-zA-Z0-9_-]*$/),
10
+ description: z.string().trim().min(1).max(300),
11
+ })
12
+ .strict();
13
+ const mediaInputSchema = z
14
+ .object({ text: z.string().trim().min(1).max(32000).optional(), image: imageSchema.optional() })
15
+ .strict()
16
+ .refine((v) => v.text !== undefined || v.image !== undefined, 'Provide text or image');
17
+ export const classifySchema = z
18
+ .object({
19
+ input: z.union([z.string().trim().min(1).max(32000), mediaInputSchema]),
20
+ classes: z
21
+ .array(classSchema)
22
+ .min(2)
23
+ .max(limits.maxClasses)
24
+ .refine((v) => new Set(v.map((c) => c.id)).size === v.length, 'Class IDs must be unique'),
25
+ instructions: z.string().trim().min(1).max(4000).optional(),
26
+ representation: representationSchema.optional(),
27
+ })
28
+ .strict();