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.
- package/README.md +24 -8
- package/dist/app/classifier-config.d.ts +30 -0
- package/dist/app/classifier-config.js +46 -0
- package/dist/app/classifier-contracts.d.ts +32 -0
- package/dist/app/classifier-contracts.js +1 -0
- package/dist/app/classifier-representation.d.ts +5 -0
- package/dist/app/classifier-representation.js +33 -0
- package/dist/app/classifier-runtime.d.ts +7 -0
- package/dist/app/classifier-runtime.js +71 -0
- package/dist/app/classifier-spatial.d.ts +31 -0
- package/dist/app/classifier-spatial.js +78 -0
- package/dist/app/cli-arguments.d.ts +2 -2
- package/dist/app/create-classifier.d.ts +4 -29
- package/dist/app/create-classifier.js +37 -237
- package/dist/cli.js +1 -1
- package/dist/entities/classification.d.ts +98 -0
- package/dist/entities/classification.js +28 -0
- package/dist/entities/contracts.d.ts +6 -471
- package/dist/entities/contracts.js +6 -97
- package/dist/entities/decision.d.ts +211 -0
- package/dist/entities/decision.js +33 -0
- package/dist/entities/geometry.d.ts +37 -0
- package/dist/entities/geometry.js +26 -0
- package/dist/entities/image.d.ts +39 -0
- package/dist/entities/image.js +15 -0
- package/dist/entities/localization.d.ts +125 -0
- package/dist/entities/localization.js +13 -0
- package/dist/entities/object-proposals.d.ts +4 -1
- package/dist/entities/segmentation-metadata.d.ts +70 -0
- package/dist/entities/segmentation-metadata.js +21 -1
- package/dist/entities/segmentation-verification.d.ts +1 -0
- package/dist/entities/segmentation.d.ts +6 -1
- package/dist/entities/validation.d.ts +2 -0
- package/dist/entities/validation.js +9 -0
- package/dist/entities/whole-object.d.ts +4 -0
- package/dist/features/choice-beam-layout.js +2 -7
- package/dist/features/proposals/presentation.d.ts +21 -0
- package/dist/features/proposals/presentation.js +122 -0
- package/dist/features/proposals/validation.d.ts +138 -0
- package/dist/features/proposals/validation.js +125 -0
- package/dist/features/segmentation/box-check.js +10 -23
- package/dist/features/segmentation/mask.js +2 -7
- package/dist/features/segmentation/region-result.d.ts +18 -0
- package/dist/features/segmentation/region-result.js +171 -0
- package/dist/features/segmentation/source-mask.d.ts +6 -0
- package/dist/features/segmentation/source-mask.js +32 -0
- package/dist/features/segmentation/validation.d.ts +9 -0
- package/dist/features/segmentation/validation.js +94 -0
- package/dist/infrastructure/decision-transport/errors.d.ts +8 -0
- package/dist/infrastructure/decision-transport/errors.js +34 -0
- package/dist/infrastructure/decision-transport/images.d.ts +1 -0
- package/dist/infrastructure/decision-transport/images.js +52 -0
- package/dist/infrastructure/{openrouter-common.d.ts → decision-transport/request.d.ts} +6 -15
- package/dist/infrastructure/decision-transport/request.js +162 -0
- package/dist/infrastructure/decision-transport/response.d.ts +1 -0
- package/dist/infrastructure/decision-transport/response.js +72 -0
- package/dist/infrastructure/decision-transport/route-identity.d.ts +11 -0
- package/dist/infrastructure/decision-transport/route-identity.js +23 -0
- package/dist/infrastructure/openrouter-vision.js +1 -1
- package/dist/infrastructure/openrouter.js +7 -19
- package/dist/infrastructure/raster.js +3 -8
- package/dist/infrastructure/segmentation/grabcut/index.d.ts +5 -1
- package/dist/infrastructure/segmentation/grabcut/index.js +6 -5
- package/dist/infrastructure/segmentation/grabcut/masks.d.ts +49 -0
- package/dist/infrastructure/segmentation/grabcut/masks.js +89 -0
- package/dist/infrastructure/segmentation/grabcut/model-asset.d.ts +4 -0
- package/dist/infrastructure/segmentation/grabcut/model-asset.js +4 -0
- package/dist/infrastructure/segmentation/grabcut/runtime.d.ts +70 -0
- package/dist/infrastructure/segmentation/grabcut/runtime.js +96 -0
- package/dist/infrastructure/segmentation/grabcut/worker-inference.d.ts +4 -43
- package/dist/infrastructure/segmentation/grabcut/worker-inference.js +4 -187
- package/dist/infrastructure/segmentation/mobile-sam/index.d.ts +3 -58
- package/dist/infrastructure/segmentation/mobile-sam/index.js +2 -472
- package/dist/infrastructure/segmentation/mobile-sam/input.d.ts +46 -0
- package/dist/infrastructure/segmentation/mobile-sam/input.js +83 -0
- package/dist/infrastructure/segmentation/mobile-sam/model-assets.d.ts +8 -0
- package/dist/infrastructure/segmentation/mobile-sam/model-assets.js +8 -0
- package/dist/infrastructure/segmentation/mobile-sam/object-proposals.d.ts +7 -0
- package/dist/infrastructure/segmentation/mobile-sam/object-proposals.js +124 -0
- package/dist/infrastructure/segmentation/mobile-sam/options.d.ts +43 -0
- package/dist/infrastructure/segmentation/mobile-sam/options.js +19 -0
- package/dist/infrastructure/segmentation/mobile-sam/proposal-masks.d.ts +26 -0
- package/dist/infrastructure/segmentation/mobile-sam/proposal-masks.js +71 -0
- package/dist/infrastructure/segmentation/mobile-sam/segmentation-masks.d.ts +112 -0
- package/dist/infrastructure/segmentation/mobile-sam/segmentation-masks.js +230 -0
- package/dist/infrastructure/segmentation/mobile-sam/segmenter.d.ts +22 -0
- package/dist/infrastructure/segmentation/mobile-sam/segmenter.js +268 -0
- package/dist/infrastructure/segmentation/mobile-sam/worker-expand.d.ts +7 -0
- package/dist/infrastructure/segmentation/mobile-sam/worker-expand.js +188 -0
- package/dist/infrastructure/segmentation/mobile-sam/worker-inference.d.ts +6 -149
- package/dist/infrastructure/segmentation/mobile-sam/worker-inference.js +6 -845
- package/dist/infrastructure/segmentation/mobile-sam/worker-models.d.ts +14 -0
- package/dist/infrastructure/segmentation/mobile-sam/worker-models.js +72 -0
- package/dist/infrastructure/segmentation/mobile-sam/worker-proposals.d.ts +7 -0
- package/dist/infrastructure/segmentation/mobile-sam/worker-proposals.js +159 -0
- package/dist/infrastructure/segmentation/mobile-sam/worker-segment.d.ts +4 -0
- package/dist/infrastructure/segmentation/mobile-sam/worker-segment.js +126 -0
- package/dist/infrastructure/segmentation/native-protocol.d.ts +59 -34
- package/dist/infrastructure/segmentation/native-protocol.js +26 -16
- package/dist/infrastructure/segmentation/proposal-presentation-input.d.ts +20 -0
- package/dist/infrastructure/segmentation/proposal-presentation-input.js +107 -0
- package/dist/infrastructure/segmentation/proposal-presentation-pixels.d.ts +8 -0
- package/dist/infrastructure/segmentation/proposal-presentation-pixels.js +93 -0
- package/dist/infrastructure/segmentation/proposal-presentation.js +10 -201
- package/dist/infrastructure/vision-chat/options.d.ts +62 -0
- package/dist/infrastructure/vision-chat/options.js +46 -0
- package/dist/infrastructure/vision-chat/request.d.ts +23 -0
- package/dist/infrastructure/vision-chat/request.js +109 -0
- package/dist/infrastructure/vision-chat/response.d.ts +3 -0
- package/dist/infrastructure/vision-chat/response.js +105 -0
- package/dist/infrastructure/vision-chat.d.ts +4 -61
- package/dist/infrastructure/vision-chat.js +8 -271
- package/dist/openai-compatible.js +1 -1
- package/dist/processes/extract-from-region.js +11 -216
- package/dist/processes/extract.d.ts +2 -8
- package/dist/processes/extract.js +12 -128
- package/dist/processes/proposal-location.js +5 -251
- package/docs/0.2.0-plan.md +3 -3
- package/docs/README.ru.md +42 -23
- package/docs/acceptance.md +5 -5
- package/docs/agents.ru.md +95 -0
- package/docs/api.md +4 -2
- package/docs/architecture.md +33 -5
- package/docs/architecture.ru.md +146 -0
- package/docs/benchmarks.md +4 -2
- package/docs/development.ru.md +141 -0
- package/docs/operations.md +4 -4
- package/docs/provenance.json +5 -1
- package/docs/publishing.md +8 -0
- package/docs/refactoring-plan.ru.md +183 -0
- package/docs/v02-usage.md +8 -6
- package/docs/vision-results.md +2 -0
- package/package.json +5 -3
- package/dist/infrastructure/boundary-view.d.ts +0 -2
- package/dist/infrastructure/boundary-view.js +0 -263
- package/dist/infrastructure/openrouter-common.js +0 -317
- package/dist/infrastructure/text-representation.d.ts +0 -23
- package/dist/infrastructure/text-representation.js +0 -226
- package/docs/evidence/v02/categorical-heldout-v4/README.md +0 -35
- 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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
43
|
-
export function
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
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
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return
|
|
264
|
-
|
|
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
|
|
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();
|