zorya-classifier 0.2.1 → 0.2.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.
- package/README.md +31 -17
- 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-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/composed-classifier.d.ts +17 -0
- package/dist/app/composed-classifier.js +1 -0
- package/dist/app/create-classifier.d.ts +9 -33
- package/dist/app/create-classifier.js +53 -245
- package/dist/cli.js +1 -1
- package/dist/entities/classification.d.ts +83 -0
- package/dist/entities/classification.js +28 -0
- package/dist/entities/contracts.d.ts +6 -475
- 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 +30 -0
- package/dist/entities/image.js +12 -0
- package/dist/entities/localization.d.ts +110 -0
- package/dist/entities/localization.js +13 -0
- package/dist/entities/object-proposals.d.ts +1 -1
- package/dist/entities/representation.d.ts +0 -183
- package/dist/entities/representation.js +1 -33
- 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 -1
- package/dist/entities/segmentation.d.ts +5 -20
- package/dist/entities/validation.d.ts +2 -0
- package/dist/entities/validation.js +9 -0
- package/dist/entities/whole-object.d.ts +6 -21
- 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 +2 -2
- package/dist/infrastructure/segmentation/grabcut/index.js +3 -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 -66
- package/dist/infrastructure/segmentation/mobile-sam/index.js +2 -478
- 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/README.ru.md +50 -33
- package/docs/acceptance.md +6 -4
- package/docs/agents.ru.md +97 -0
- package/docs/api.md +4 -4
- package/docs/architecture.md +33 -7
- package/docs/architecture.ru.md +146 -0
- package/docs/benchmarks.md +3 -1
- package/docs/development.ru.md +141 -0
- package/docs/migrations/experimental-text-image-removal.ru.md +9 -0
- package/docs/provenance.json +288 -1
- package/docs/publishing.md +5 -3
- package/docs/refactoring-plan.ru.md +183 -0
- package/docs/v02-usage.md +8 -8
- package/docs/vision-results.md +1 -1
- 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
|
@@ -1,77 +1,35 @@
|
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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;
|
|
12
|
+
import { validateClassifierOptions } from './classifier-config.js';
|
|
13
|
+
import { createClassifierRuntime, parseOperationSignal } from './classifier-runtime.js';
|
|
14
|
+
import { createClassifierSpatial } from './classifier-spatial.js';
|
|
15
|
+
function prepareResearchOperationInput(rest, research) {
|
|
16
|
+
if (!research || rest.representation === undefined)
|
|
17
|
+
return { rest, port: undefined };
|
|
18
|
+
if (!research.acceptsRepresentation(rest.representation))
|
|
19
|
+
return { rest, port: undefined };
|
|
20
|
+
return {
|
|
21
|
+
rest: { ...rest, representation: { type: 'vision' } },
|
|
22
|
+
port: research.representationPort,
|
|
23
|
+
};
|
|
38
24
|
}
|
|
39
25
|
export function createClassifier(options) {
|
|
40
26
|
return composeClassifier(options);
|
|
41
27
|
}
|
|
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);
|
|
28
|
+
/** Internal composition seam for benchmark helpers; not re-exported from public entry points. */
|
|
29
|
+
export function composeClassifier(options, research, boundaryView, intervalPolicy) {
|
|
30
|
+
const { config, ports } = validateClassifierOptions(options);
|
|
31
|
+
const { segmenter, objectProposals } = ports;
|
|
32
|
+
const runtime = createClassifierRuntime(config);
|
|
75
33
|
const raster = createSharpRaster();
|
|
76
34
|
const cutoutRaster = createSharpCutoutRaster();
|
|
77
35
|
const boxCheck = segmenter
|
|
@@ -91,146 +49,46 @@ function composeClassifier(options, experimental, boundaryView, intervalPolicy)
|
|
|
91
49
|
presentation: createSharpObjectProposalPresentation(),
|
|
92
50
|
}
|
|
93
51
|
: undefined;
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
}
|
|
52
|
+
const spatial = createClassifierSpatial({
|
|
53
|
+
getDecision: () => options.decision,
|
|
54
|
+
raster,
|
|
55
|
+
cutoutRaster,
|
|
56
|
+
segmenter,
|
|
57
|
+
boxCheck,
|
|
58
|
+
proposalDependencies,
|
|
59
|
+
boundaryView,
|
|
60
|
+
intervalPolicy,
|
|
61
|
+
});
|
|
185
62
|
async function locate(input) {
|
|
186
|
-
|
|
187
|
-
throw new ClassifierError('DISPOSED', 'Classifier has been disposed');
|
|
188
|
-
gate.assertAvailable();
|
|
63
|
+
runtime.assertAcceptingOperations();
|
|
189
64
|
if (!input || typeof input !== 'object')
|
|
190
65
|
throw new ClassifierError('INVALID_INPUT', 'Expected locate input object');
|
|
191
66
|
const { signal, ...rest } = input;
|
|
192
|
-
const parent =
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
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
|
-
}
|
|
204
|
-
}
|
|
205
|
-
const parsed = parse(locateSchema, rest);
|
|
206
|
-
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();
|
|
67
|
+
const parent = parseOperationSignal(signal);
|
|
68
|
+
const { rest: rewritten, port } = prepareResearchOperationInput(rest, research);
|
|
69
|
+
if (rewritten.mode === 'whole-object') {
|
|
70
|
+
const parsed = parse(wholeObjectSchema, rewritten);
|
|
71
|
+
return runtime.withOwnedInput(parsed.image.byteLength, parent, () => ({ ...parsed, image: new Uint8Array(parsed.image) }), (snapshot, s) => spatial.locateWhole(snapshot, s, port));
|
|
214
72
|
}
|
|
73
|
+
const parsed = parse(locateSchema, rewritten);
|
|
74
|
+
return runtime.withOwnedInput(parsed.image.byteLength, parent, () => ({ ...parsed, image: new Uint8Array(parsed.image) }), (snapshot, s) => locateInput(snapshot, options.decision, raster, s, options.refinement, port));
|
|
215
75
|
}
|
|
216
76
|
return {
|
|
217
77
|
async classify(input) {
|
|
218
|
-
|
|
219
|
-
throw new ClassifierError('DISPOSED', 'Classifier has been disposed');
|
|
220
|
-
gate.assertAvailable();
|
|
78
|
+
runtime.assertAcceptingOperations();
|
|
221
79
|
if (!input || typeof input !== 'object')
|
|
222
80
|
throw new ClassifierError('INVALID_INPUT', 'Expected classify input object');
|
|
223
81
|
const { signal, ...rest } = input;
|
|
224
|
-
const parent =
|
|
225
|
-
const
|
|
82
|
+
const parent = parseOperationSignal(signal);
|
|
83
|
+
const { rest: rewritten, port } = prepareResearchOperationInput(rest, research);
|
|
84
|
+
const parsed = parse(classifySchema, rewritten);
|
|
226
85
|
const hasImage = typeof parsed.input !== 'string' && parsed.input.image !== undefined;
|
|
227
|
-
const representation = hasImage ?
|
|
86
|
+
const representation = hasImage ? port : undefined;
|
|
228
87
|
const byteCount = typeof parsed.input === 'string'
|
|
229
88
|
? Buffer.byteLength(parsed.input)
|
|
230
89
|
: (parsed.input.image?.byteLength ?? 0) + Buffer.byteLength(parsed.input.text ?? '');
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const media = typeof parsed.input === 'string'
|
|
90
|
+
return runtime.withOwnedInput(byteCount, parent, () => {
|
|
91
|
+
return typeof parsed.input === 'string'
|
|
234
92
|
? parsed.input
|
|
235
93
|
: {
|
|
236
94
|
...parsed.input,
|
|
@@ -238,75 +96,25 @@ function composeClassifier(options, experimental, boundaryView, intervalPolicy)
|
|
|
238
96
|
? {}
|
|
239
97
|
: { image: new Uint8Array(parsed.input.image) }),
|
|
240
98
|
};
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
finally {
|
|
244
|
-
release();
|
|
245
|
-
}
|
|
99
|
+
}, (media, s) => classifyInput({ ...parsed, input: media }, options.decision, raster, s, representation));
|
|
246
100
|
},
|
|
247
101
|
locate,
|
|
248
102
|
async extract(input) {
|
|
249
|
-
|
|
250
|
-
throw new ClassifierError('DISPOSED', 'Classifier has been disposed');
|
|
251
|
-
gate.assertAvailable();
|
|
103
|
+
runtime.assertAcceptingOperations();
|
|
252
104
|
if (!input || typeof input !== 'object')
|
|
253
105
|
throw new ClassifierError('INVALID_INPUT', 'Expected extract input object');
|
|
254
106
|
const { signal, ...rest } = input;
|
|
255
|
-
const parent =
|
|
256
|
-
const
|
|
107
|
+
const parent = parseOperationSignal(signal);
|
|
108
|
+
const { rest: rewritten, port } = prepareResearchOperationInput(rest, research);
|
|
109
|
+
const parsed = parse(extractSchema, rewritten);
|
|
257
110
|
if (!segmenter && !proposalDependencies)
|
|
258
111
|
throw new ClassifierError('UNSUPPORTED', 'extract requires an explicit SegmentationPort or ObjectProposalPort');
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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());
|
|
112
|
+
return runtime.withOwnedInput(parsed.image.byteLength, parent, () => ({ ...parsed, image: new Uint8Array(parsed.image) }), (snapshot, s) => {
|
|
113
|
+
if (segmenter)
|
|
114
|
+
return spatial.extractRegion(snapshot, s, port);
|
|
115
|
+
return spatial.extractWithProposals(snapshot, s, port);
|
|
116
|
+
});
|
|
310
117
|
},
|
|
118
|
+
dispose: () => runtime.dispose(),
|
|
311
119
|
};
|
|
312
120
|
}
|
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] (
|
|
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] (spatial search; grid cells are not whole-object boxes)\nzorya-classifier extract <image> <target> --output <prefix> --backend mobile-sam --encoder <path> --decoder <path> [--provider cpu|dml] (extraction with caller-owned backend)\nzorya-classifier extract <image> <target> --output <prefix> --backend grabcut --opencv <path> (extraction with caller-owned backend)\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 available—model, provider and native assets remain caller-supplied. 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,83 @@
|
|
|
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.ZodObject<{
|
|
48
|
+
type: z.ZodLiteral<"vision">;
|
|
49
|
+
}, "strict", z.ZodTypeAny, {
|
|
50
|
+
type: "vision";
|
|
51
|
+
}, {
|
|
52
|
+
type: "vision";
|
|
53
|
+
}>>;
|
|
54
|
+
}, "strict", z.ZodTypeAny, {
|
|
55
|
+
input: string | {
|
|
56
|
+
text?: string | undefined;
|
|
57
|
+
image?: Uint8Array<ArrayBufferLike> | undefined;
|
|
58
|
+
};
|
|
59
|
+
classes: {
|
|
60
|
+
id: string;
|
|
61
|
+
description: string;
|
|
62
|
+
}[];
|
|
63
|
+
instructions?: string | undefined;
|
|
64
|
+
representation?: {
|
|
65
|
+
type: "vision";
|
|
66
|
+
} | undefined;
|
|
67
|
+
}, {
|
|
68
|
+
input: string | {
|
|
69
|
+
text?: string | undefined;
|
|
70
|
+
image?: Uint8Array<ArrayBufferLike> | undefined;
|
|
71
|
+
};
|
|
72
|
+
classes: {
|
|
73
|
+
id: string;
|
|
74
|
+
description: string;
|
|
75
|
+
}[];
|
|
76
|
+
instructions?: string | undefined;
|
|
77
|
+
representation?: {
|
|
78
|
+
type: "vision";
|
|
79
|
+
} | undefined;
|
|
80
|
+
}>;
|
|
81
|
+
export type ClassifyInput = z.input<typeof classifySchema> & {
|
|
82
|
+
signal?: AbortSignal;
|
|
83
|
+
};
|
|
@@ -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();
|