@dejadex/web 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +214 -2
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +9 -0
- package/dist/errors.js.map +1 -0
- package/dist/geometry/homography.d.ts +75 -0
- package/dist/geometry/homography.d.ts.map +1 -0
- package/dist/geometry/homography.js +146 -0
- package/dist/geometry/homography.js.map +1 -0
- package/dist/identify/assets.d.ts +39 -0
- package/dist/identify/assets.d.ts.map +1 -0
- package/dist/identify/assets.js +167 -0
- package/dist/identify/assets.js.map +1 -0
- package/dist/identify/decode.d.ts +6 -0
- package/dist/identify/decode.d.ts.map +1 -0
- package/dist/identify/decode.js +53 -0
- package/dist/identify/decode.js.map +1 -0
- package/dist/identify/engine.d.ts +32 -0
- package/dist/identify/engine.d.ts.map +1 -0
- package/dist/identify/engine.js +127 -0
- package/dist/identify/engine.js.map +1 -0
- package/dist/identify/identifier.d.ts +81 -0
- package/dist/identify/identifier.d.ts.map +1 -0
- package/dist/identify/identifier.js +327 -0
- package/dist/identify/identifier.js.map +1 -0
- package/dist/identify/manifest.d.ts +60 -0
- package/dist/identify/manifest.d.ts.map +1 -0
- package/dist/identify/manifest.js +211 -0
- package/dist/identify/manifest.js.map +1 -0
- package/dist/identify/preprocess.d.ts +52 -0
- package/dist/identify/preprocess.d.ts.map +1 -0
- package/dist/identify/preprocess.js +195 -0
- package/dist/identify/preprocess.js.map +1 -0
- package/dist/identify/router.d.ts +60 -0
- package/dist/identify/router.d.ts.map +1 -0
- package/dist/identify/router.js +83 -0
- package/dist/identify/router.js.map +1 -0
- package/dist/identify/runtime.d.ts +69 -0
- package/dist/identify/runtime.d.ts.map +1 -0
- package/dist/identify/runtime.js +140 -0
- package/dist/identify/runtime.js.map +1 -0
- package/dist/identify/search.d.ts +35 -0
- package/dist/identify/search.d.ts.map +1 -0
- package/dist/identify/search.js +99 -0
- package/dist/identify/search.js.map +1 -0
- package/dist/identify/server.d.ts +60 -0
- package/dist/identify/server.d.ts.map +1 -0
- package/dist/identify/server.js +149 -0
- package/dist/identify/server.js.map +1 -0
- package/dist/identify/shard.d.ts +31 -0
- package/dist/identify/shard.d.ts.map +1 -0
- package/dist/identify/shard.js +146 -0
- package/dist/identify/shard.js.map +1 -0
- package/dist/identify/types.d.ts +107 -0
- package/dist/identify/types.d.ts.map +1 -0
- package/dist/identify/types.js +7 -0
- package/dist/identify/types.js.map +1 -0
- package/dist/identify/worker.d.ts +2 -0
- package/dist/identify/worker.d.ts.map +1 -0
- package/dist/identify/worker.js +13 -0
- package/dist/identify/worker.js.map +1 -0
- package/dist/identify/workerProtocol.d.ts +54 -0
- package/dist/identify/workerProtocol.d.ts.map +1 -0
- package/dist/identify/workerProtocol.js +113 -0
- package/dist/identify/workerProtocol.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/lease/verify.d.ts +69 -0
- package/dist/lease/verify.d.ts.map +1 -0
- package/dist/lease/verify.js +235 -0
- package/dist/lease/verify.js.map +1 -0
- package/dist/scanner.d.ts +10 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +58 -0
- package/dist/scanner.js.map +1 -0
- package/dist/types.d.ts +180 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/usage/meter.d.ts +83 -0
- package/dist/usage/meter.d.ts.map +1 -0
- package/dist/usage/meter.js +111 -0
- package/dist/usage/meter.js.map +1 -0
- package/dist/usage/store.d.ts +52 -0
- package/dist/usage/store.d.ts.map +1 -0
- package/dist/usage/store.js +198 -0
- package/dist/usage/store.js.map +1 -0
- package/package.json +64 -4
- package/src/errors.ts +24 -0
- package/src/geometry/homography.ts +178 -0
- package/src/identify/assets.ts +189 -0
- package/src/identify/decode.ts +54 -0
- package/src/identify/engine.ts +167 -0
- package/src/identify/identifier.ts +419 -0
- package/src/identify/manifest.ts +240 -0
- package/src/identify/preprocess.ts +227 -0
- package/src/identify/router.ts +115 -0
- package/src/identify/runtime.ts +197 -0
- package/src/identify/search.ts +115 -0
- package/src/identify/server.ts +167 -0
- package/src/identify/shard.ts +154 -0
- package/src/identify/types.ts +122 -0
- package/src/identify/worker.ts +13 -0
- package/src/identify/workerProtocol.ts +136 -0
- package/src/index.ts +71 -0
- package/src/lease/verify.ts +252 -0
- package/src/scanner.ts +56 -0
- package/src/types.ts +196 -0
- package/src/usage/meter.ts +165 -0
- package/src/usage/store.ts +245 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brute-force cosine top-K over a shard, and the rules that turn the ranking
|
|
3
|
+
* into an answer. Plain loops over typed arrays: no DOM, no allocation per row,
|
|
4
|
+
* the same in a Worker as anywhere else.
|
|
5
|
+
*
|
|
6
|
+
* Rows and queries are L2-normalised, so a dot product is the cosine. A crop
|
|
7
|
+
* embedded at several orientations scores each row by the best of them (the
|
|
8
|
+
* max-pool the server does).
|
|
9
|
+
*/
|
|
10
|
+
import type { IdentifyResult } from './types.js';
|
|
11
|
+
export interface Ranked {
|
|
12
|
+
/** Row indexes, best first. */
|
|
13
|
+
readonly rows: Int32Array;
|
|
14
|
+
readonly scores: Float32Array;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The `k` best rows for a set of query vectors (`queries.length / dim` of
|
|
18
|
+
* them), each row scored by its best dot product with any query. Ties keep the
|
|
19
|
+
* lower row first.
|
|
20
|
+
*/
|
|
21
|
+
export declare function topK(matrix: Float32Array, dim: number, queries: Float32Array, k: number): Ranked;
|
|
22
|
+
/** In place: scales each `dim`-wide row of `v` to unit length (a zero row stays zero). */
|
|
23
|
+
export declare function l2NormaliseRows(v: Float32Array, dim: number): Float32Array;
|
|
24
|
+
export interface Thresholds {
|
|
25
|
+
/** `kind: 'not_a_card'` when the best score is below this. */
|
|
26
|
+
readonly notACardBelow: number;
|
|
27
|
+
/** `confident` when top1 − top2 ≥ this. */
|
|
28
|
+
readonly confidentMargin: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The answer for one crop from its ranking. `ranked` holds at least
|
|
32
|
+
* `max(top, 2)` rows where the shard has them: the margin needs a second one.
|
|
33
|
+
*/
|
|
34
|
+
export declare function toResult(index: number, ranked: Ranked, ids: readonly string[], top: number, t: Thresholds): IdentifyResult;
|
|
35
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/identify/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAqB,cAAc,EAAE,MAAM,YAAY,CAAA;AAEnE,MAAM,WAAW,MAAM;IACrB,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA+ChG;AAED,0FAA0F;AAC1F,wBAAgB,eAAe,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAQ1E;AAED,MAAM,WAAW,UAAU;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,2CAA2C;IAC3C,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CACjC;AAKD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,GAAG,cAAc,CAkB1H"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `k` best rows for a set of query vectors (`queries.length / dim` of
|
|
3
|
+
* them), each row scored by its best dot product with any query. Ties keep the
|
|
4
|
+
* lower row first.
|
|
5
|
+
*/
|
|
6
|
+
export function topK(matrix, dim, queries, k) {
|
|
7
|
+
if (!Number.isInteger(dim) || dim <= 0 || matrix.length % dim !== 0)
|
|
8
|
+
throw new RangeError('matrix is not rows × dim');
|
|
9
|
+
if (queries.length === 0 || queries.length % dim !== 0)
|
|
10
|
+
throw new RangeError('queries are not n × dim');
|
|
11
|
+
const rowCount = matrix.length / dim;
|
|
12
|
+
const q = queries.length / dim;
|
|
13
|
+
const want = Math.min(Math.max(0, Math.floor(k)), rowCount);
|
|
14
|
+
const bestRows = new Int32Array(want);
|
|
15
|
+
const bestScores = new Float32Array(want).fill(Number.NEGATIVE_INFINITY);
|
|
16
|
+
if (want === 0)
|
|
17
|
+
return { rows: bestRows, scores: bestScores };
|
|
18
|
+
let floor = Number.NEGATIVE_INFINITY;
|
|
19
|
+
let filled = 0;
|
|
20
|
+
for (let r = 0; r < rowCount; r++) {
|
|
21
|
+
const base = r * dim;
|
|
22
|
+
let score;
|
|
23
|
+
if (q === 2) {
|
|
24
|
+
let a = 0;
|
|
25
|
+
let b = 0;
|
|
26
|
+
for (let i = 0; i < dim; i++) {
|
|
27
|
+
const v = matrix[base + i];
|
|
28
|
+
a += v * queries[i];
|
|
29
|
+
b += v * queries[dim + i];
|
|
30
|
+
}
|
|
31
|
+
score = a > b ? a : b;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
score = Number.NEGATIVE_INFINITY;
|
|
35
|
+
for (let j = 0; j < q; j++) {
|
|
36
|
+
let s = 0;
|
|
37
|
+
const qb = j * dim;
|
|
38
|
+
for (let i = 0; i < dim; i++)
|
|
39
|
+
s += matrix[base + i] * queries[qb + i];
|
|
40
|
+
if (s > score)
|
|
41
|
+
score = s;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (filled === want && score <= floor)
|
|
45
|
+
continue;
|
|
46
|
+
// Insert into the sorted list; strictly greater moves up, so ties keep the earlier row.
|
|
47
|
+
let at = filled === want ? want - 1 : filled;
|
|
48
|
+
while (at > 0 && score > bestScores[at - 1]) {
|
|
49
|
+
bestScores[at] = bestScores[at - 1];
|
|
50
|
+
bestRows[at] = bestRows[at - 1];
|
|
51
|
+
at--;
|
|
52
|
+
}
|
|
53
|
+
bestScores[at] = score;
|
|
54
|
+
bestRows[at] = r;
|
|
55
|
+
if (filled < want)
|
|
56
|
+
filled++;
|
|
57
|
+
floor = bestScores[want - 1];
|
|
58
|
+
}
|
|
59
|
+
return { rows: bestRows, scores: bestScores };
|
|
60
|
+
}
|
|
61
|
+
/** In place: scales each `dim`-wide row of `v` to unit length (a zero row stays zero). */
|
|
62
|
+
export function l2NormaliseRows(v, dim) {
|
|
63
|
+
for (let base = 0; base < v.length; base += dim) {
|
|
64
|
+
let s = 0;
|
|
65
|
+
for (let i = 0; i < dim; i++)
|
|
66
|
+
s += v[base + i] ** 2;
|
|
67
|
+
const inv = 1 / Math.max(Math.sqrt(s), 1e-12);
|
|
68
|
+
for (let i = 0; i < dim; i++)
|
|
69
|
+
v[base + i] = v[base + i] * inv;
|
|
70
|
+
}
|
|
71
|
+
return v;
|
|
72
|
+
}
|
|
73
|
+
/** Scores in answers carry six decimals, as the server's do. */
|
|
74
|
+
const round6 = (x) => Math.round(x * 1e6) / 1e6;
|
|
75
|
+
/**
|
|
76
|
+
* The answer for one crop from its ranking. `ranked` holds at least
|
|
77
|
+
* `max(top, 2)` rows where the shard has them: the margin needs a second one.
|
|
78
|
+
*/
|
|
79
|
+
export function toResult(index, ranked, ids, top, t) {
|
|
80
|
+
const s0 = ranked.scores[0];
|
|
81
|
+
const s1 = ranked.scores[1];
|
|
82
|
+
const notACard = s0 === undefined || s0 < t.notACardBelow;
|
|
83
|
+
const margin = s0 !== undefined && s1 !== undefined ? s0 - s1 : Number.POSITIVE_INFINITY;
|
|
84
|
+
const candidates = [];
|
|
85
|
+
const n = Math.min(top, ranked.rows.length);
|
|
86
|
+
for (let i = 0; i < n; i++) {
|
|
87
|
+
const id = ids[ranked.rows[i]];
|
|
88
|
+
if (id === undefined)
|
|
89
|
+
throw new RangeError(`row ${ranked.rows[i]} has no id`);
|
|
90
|
+
candidates.push({ cardId: id, score: round6(ranked.scores[i]) });
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
index,
|
|
94
|
+
kind: notACard ? 'not_a_card' : 'card',
|
|
95
|
+
confident: !notACard && margin >= t.confidentMargin,
|
|
96
|
+
candidates,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/identify/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type IdentifyPurpose, type IdentifyResult } from './types.js';
|
|
2
|
+
export type ServerFetch = (input: string, init: {
|
|
3
|
+
method: 'POST';
|
|
4
|
+
body: FormData;
|
|
5
|
+
headers: Record<string, string>;
|
|
6
|
+
signal: AbortSignal;
|
|
7
|
+
}) => Promise<Response>;
|
|
8
|
+
export interface ServerRequest {
|
|
9
|
+
readonly endpoint: string;
|
|
10
|
+
readonly crops: readonly Blob[];
|
|
11
|
+
/** The lease, as bearer. */
|
|
12
|
+
readonly token: string;
|
|
13
|
+
readonly top: number;
|
|
14
|
+
readonly session?: string;
|
|
15
|
+
readonly purpose?: IdentifyPurpose;
|
|
16
|
+
readonly timeoutMs: number;
|
|
17
|
+
readonly signal?: AbortSignal;
|
|
18
|
+
readonly fetch: ServerFetch;
|
|
19
|
+
}
|
|
20
|
+
export type ServerFailureKind =
|
|
21
|
+
/** The deadline passed. */
|
|
22
|
+
'timeout'
|
|
23
|
+
/** The caller aborted. */
|
|
24
|
+
| 'aborted'
|
|
25
|
+
/** No answer at all: offline, DNS, CORS, connection reset. */
|
|
26
|
+
| 'network'
|
|
27
|
+
/** 401 / 403 / 409: this lease (its origin, domains, tenant or model version) is refused. */
|
|
28
|
+
| 'refused'
|
|
29
|
+
/** 429, with `retryAfterMs`. */
|
|
30
|
+
| 'rate_limited'
|
|
31
|
+
/** 5xx. */
|
|
32
|
+
| 'server_error'
|
|
33
|
+
/** Another 4xx: something about this request (e.g. a crop that is not a JPEG). */
|
|
34
|
+
| 'bad_request'
|
|
35
|
+
/** A 2xx whose body is not the contract. */
|
|
36
|
+
| 'malformed';
|
|
37
|
+
export type ServerAnswer = {
|
|
38
|
+
readonly ok: true;
|
|
39
|
+
readonly modelVersion: string;
|
|
40
|
+
readonly results: readonly IdentifyResult[];
|
|
41
|
+
} | {
|
|
42
|
+
readonly ok: false;
|
|
43
|
+
readonly kind: ServerFailureKind;
|
|
44
|
+
readonly status?: number;
|
|
45
|
+
readonly retryAfterMs?: number;
|
|
46
|
+
readonly detail: string;
|
|
47
|
+
};
|
|
48
|
+
/** `Retry-After` as delay-seconds or an HTTP date; null when absent or unreadable. */
|
|
49
|
+
export declare function parseRetryAfter(value: string | null, now: number): number | null;
|
|
50
|
+
export declare function postIdentify(req: ServerRequest): Promise<ServerAnswer>;
|
|
51
|
+
/**
|
|
52
|
+
* The 200 body, checked field by field. Returns the results in index order,
|
|
53
|
+
* or a string naming the first thing wrong. A crop with no result is allowed:
|
|
54
|
+
* it stays unidentified, as on any failure.
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseAnswer(data: unknown, cropCount: number, top: number): {
|
|
57
|
+
modelVersion: string;
|
|
58
|
+
results: IdentifyResult[];
|
|
59
|
+
} | string;
|
|
60
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/identify/server.ts"],"names":[],"mappings":"AAOA,OAAO,EAAsD,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,CAAA;AAE1H,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAE9J,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAA;IAC/B,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,CAAA;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;CAC5B;AAED,MAAM,MAAM,iBAAiB;AAC3B,2BAA2B;AACzB,SAAS;AACX,0BAA0B;GACxB,SAAS;AACX,8DAA8D;GAC5D,SAAS;AACX,6FAA6F;GAC3F,SAAS;AACX,gCAAgC;GAC9B,cAAc;AAChB,WAAW;GACT,cAAc;AAChB,kFAAkF;GAChF,aAAa;AACf,4CAA4C;GAC1C,WAAW,CAAA;AAEf,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAA;CAAE,GACjG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAI/I,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhF;AAED,wBAAsB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CA+C5E;AAaD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,cAAc,EAAE,CAAA;CAAE,GAAG,MAAM,CA4CvI"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client of `POST /v1/identify`: crops out, the same answer shape the
|
|
3
|
+
* device produces back. One call is one request, bounded by one deadline that
|
|
4
|
+
* covers the headers and the body; the answer is validated field by field,
|
|
5
|
+
* never trusted.
|
|
6
|
+
*/
|
|
7
|
+
import { anySignal } from './assets.js';
|
|
8
|
+
import { MAX_TOP } from './types.js';
|
|
9
|
+
const errorText = (err) => (err instanceof Error ? err.message || err.name : String(err));
|
|
10
|
+
/** `Retry-After` as delay-seconds or an HTTP date; null when absent or unreadable. */
|
|
11
|
+
export function parseRetryAfter(value, now) {
|
|
12
|
+
if (value === null)
|
|
13
|
+
return null;
|
|
14
|
+
const v = value.trim();
|
|
15
|
+
if (/^\d+$/.test(v))
|
|
16
|
+
return Number(v) * 1000;
|
|
17
|
+
const at = Date.parse(v);
|
|
18
|
+
return Number.isNaN(at) ? null : Math.max(0, at - now);
|
|
19
|
+
}
|
|
20
|
+
export async function postIdentify(req) {
|
|
21
|
+
if (req.signal?.aborted)
|
|
22
|
+
return { ok: false, kind: 'aborted', detail: 'aborted' };
|
|
23
|
+
const deadline = AbortSignal.timeout(req.timeoutMs);
|
|
24
|
+
const signal = anySignal([deadline, req.signal]);
|
|
25
|
+
const body = new FormData();
|
|
26
|
+
req.crops.forEach((crop, i) => body.append(`crop${i}`, crop, `crop${i}.jpg`));
|
|
27
|
+
body.append('top', String(req.top));
|
|
28
|
+
if (req.session !== undefined)
|
|
29
|
+
body.append('session', req.session);
|
|
30
|
+
if (req.purpose !== undefined)
|
|
31
|
+
body.append('purpose', req.purpose);
|
|
32
|
+
// Which signal fired decides the outcome, not the error's name: runtimes
|
|
33
|
+
// disagree on what they throw for each.
|
|
34
|
+
const failed = (kind, detail, extra = {}) => {
|
|
35
|
+
if (req.signal?.aborted)
|
|
36
|
+
return { ok: false, kind: 'aborted', detail: 'aborted' };
|
|
37
|
+
if (deadline.aborted)
|
|
38
|
+
return { ok: false, kind: 'timeout', detail: `no answer within ${req.timeoutMs} ms` };
|
|
39
|
+
return { ok: false, kind, detail, ...extra };
|
|
40
|
+
};
|
|
41
|
+
let res;
|
|
42
|
+
try {
|
|
43
|
+
res = await req.fetch(req.endpoint, { method: 'POST', body, headers: { Authorization: `Bearer ${req.token}` }, signal });
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return failed('network', errorText(err));
|
|
47
|
+
}
|
|
48
|
+
let data;
|
|
49
|
+
try {
|
|
50
|
+
// Inside the same deadline as the request: a slow body is a late answer.
|
|
51
|
+
const type = res.headers.get('content-type') ?? '';
|
|
52
|
+
if (/[/+]json\b/i.test(type))
|
|
53
|
+
data = await res.json();
|
|
54
|
+
else
|
|
55
|
+
await res.text(); // drained, so a slow body still counts against the deadline
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
return failed(res.ok ? 'malformed' : 'server_error', `unreadable answer: ${errorText(err)}`, { status: res.status });
|
|
59
|
+
}
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
const detail = problemText(res.status, data);
|
|
62
|
+
if (res.status === 401 || res.status === 403 || res.status === 409)
|
|
63
|
+
return failed('refused', detail, { status: res.status });
|
|
64
|
+
if (res.status === 429) {
|
|
65
|
+
const retryAfterMs = parseRetryAfter(res.headers.get('retry-after'), Date.now()) ?? 10_000;
|
|
66
|
+
return failed('rate_limited', detail, { status: res.status, retryAfterMs });
|
|
67
|
+
}
|
|
68
|
+
if (res.status >= 500)
|
|
69
|
+
return failed('server_error', detail, { status: res.status });
|
|
70
|
+
return failed('bad_request', detail, { status: res.status });
|
|
71
|
+
}
|
|
72
|
+
const parsed = parseAnswer(data, req.crops.length, req.top);
|
|
73
|
+
if (typeof parsed === 'string')
|
|
74
|
+
return failed('malformed', `malformed answer: ${parsed}`, { status: res.status });
|
|
75
|
+
return { ok: true, ...parsed };
|
|
76
|
+
}
|
|
77
|
+
const isRecord = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
78
|
+
function problemText(status, data) {
|
|
79
|
+
if (isRecord(data)) {
|
|
80
|
+
const code = typeof data.code === 'string' ? data.code : `HTTP_${status}`;
|
|
81
|
+
const detail = typeof data.detail === 'string' ? data.detail : typeof data.title === 'string' ? data.title : '';
|
|
82
|
+
return detail ? `${code}: ${detail}` : code;
|
|
83
|
+
}
|
|
84
|
+
return `HTTP_${status}`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The 200 body, checked field by field. Returns the results in index order,
|
|
88
|
+
* or a string naming the first thing wrong. A crop with no result is allowed:
|
|
89
|
+
* it stays unidentified, as on any failure.
|
|
90
|
+
*/
|
|
91
|
+
export function parseAnswer(data, cropCount, top) {
|
|
92
|
+
if (!isRecord(data) || !Array.isArray(data.results))
|
|
93
|
+
return 'expected { modelVersion, results: [...] }';
|
|
94
|
+
if (typeof data.modelVersion !== 'string' || data.modelVersion.length === 0)
|
|
95
|
+
return 'modelVersion is missing';
|
|
96
|
+
if (data.results.length > cropCount)
|
|
97
|
+
return `${data.results.length} results for ${cropCount} crops`;
|
|
98
|
+
const limit = Math.min(Math.max(top, 1), MAX_TOP);
|
|
99
|
+
const seen = new Set();
|
|
100
|
+
const out = [];
|
|
101
|
+
for (const [at, raw] of data.results.entries()) {
|
|
102
|
+
if (!isRecord(raw))
|
|
103
|
+
return `results[${at}] is not an object`;
|
|
104
|
+
const { index, confident, candidates, kind, identificationId } = raw;
|
|
105
|
+
if (typeof index !== 'number' || !Number.isInteger(index) || index < 0 || index >= cropCount)
|
|
106
|
+
return `results[${at}].index is not a crop index`;
|
|
107
|
+
if (seen.has(index))
|
|
108
|
+
return `index ${index} answered twice`;
|
|
109
|
+
seen.add(index);
|
|
110
|
+
// An absent kind is a card (the stand-in's earlier answers had none).
|
|
111
|
+
if (kind !== undefined && kind !== 'card' && kind !== 'not_a_card')
|
|
112
|
+
return `results[${at}].kind is not card or not_a_card`;
|
|
113
|
+
const k = kind === 'not_a_card' ? 'not_a_card' : 'card';
|
|
114
|
+
if (typeof confident !== 'boolean')
|
|
115
|
+
return `results[${at}].confident is not a boolean`;
|
|
116
|
+
if (k === 'not_a_card' && confident)
|
|
117
|
+
return `results[${at}] is a confident not_a_card`;
|
|
118
|
+
if (!Array.isArray(candidates))
|
|
119
|
+
return `results[${at}].candidates is not an array`;
|
|
120
|
+
if (candidates.length > limit)
|
|
121
|
+
return `results[${at}] has ${candidates.length} candidates for top ${limit}`;
|
|
122
|
+
if (identificationId !== undefined && identificationId !== null && (typeof identificationId !== 'string' || identificationId.length === 0)) {
|
|
123
|
+
return `results[${at}].identificationId is not a string`;
|
|
124
|
+
}
|
|
125
|
+
const parsed = [];
|
|
126
|
+
for (const [c, cand] of candidates.entries()) {
|
|
127
|
+
if (!isRecord(cand) || typeof cand.cardId !== 'string' || cand.cardId.length === 0)
|
|
128
|
+
return `results[${at}].candidates[${c}].cardId is not a string`;
|
|
129
|
+
if (typeof cand.score !== 'number' || !Number.isFinite(cand.score))
|
|
130
|
+
return `results[${at}].candidates[${c}].score is not a number`;
|
|
131
|
+
const prev = parsed[parsed.length - 1];
|
|
132
|
+
if (prev && cand.score > prev.score)
|
|
133
|
+
return `results[${at}].candidates are not best first`;
|
|
134
|
+
parsed.push({ cardId: cand.cardId, score: cand.score });
|
|
135
|
+
}
|
|
136
|
+
if (confident && parsed.length === 0)
|
|
137
|
+
return `results[${at}] is confident with no candidate`;
|
|
138
|
+
out.push({
|
|
139
|
+
index,
|
|
140
|
+
kind: k,
|
|
141
|
+
confident,
|
|
142
|
+
candidates: parsed,
|
|
143
|
+
...(typeof identificationId === 'string' ? { identificationId } : {}),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
out.sort((a, b) => a.index - b.index);
|
|
147
|
+
return { modelVersion: data.modelVersion, results: out };
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/identify/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,OAAO,EAAwF,MAAM,YAAY,CAAA;AAuC1H,MAAM,SAAS,GAAG,CAAC,GAAY,EAAU,EAAE,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;AAE1G,sFAAsF;AACtF,MAAM,UAAU,eAAe,CAAC,KAAoB,EAAE,GAAW;IAC/D,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC/B,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IACtB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;IAC5C,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACxB,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,GAAG,CAAC,CAAA;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAkB;IACnD,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;IACjF,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IACnD,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;IAEhD,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAA;IAC3B,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;IAC7E,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;IACnC,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS;QAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAClE,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS;QAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IAElE,yEAAyE;IACzE,wCAAwC;IACxC,MAAM,MAAM,GAAG,CAAC,IAAuB,EAAE,MAAc,EAAE,QAAoD,EAAE,EAAgB,EAAE;QAC/H,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;QACjF,IAAI,QAAQ,CAAC,OAAO;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,oBAAoB,GAAG,CAAC,SAAS,KAAK,EAAE,CAAA;QAC3G,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,CAAA;IAC9C,CAAC,CAAA;IAED,IAAI,GAAa,CAAA;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,GAAG,CAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;IAC1H,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAA;IAC1C,CAAC;IACD,IAAI,IAAa,CAAA;IACjB,IAAI,CAAC;QACH,yEAAyE;QACzE,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAA;QAClD,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;;YAChD,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA,CAAC,4DAA4D;IACpF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,EAAE,sBAAsB,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;IACtH,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QAC5C,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;QAC5H,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,MAAM,YAAY,GAAG,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,MAAM,CAAA;YAC1F,OAAO,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CAAA;QAC7E,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG;YAAE,OAAO,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;QACpF,OAAO,MAAM,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;IAC9D,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAA;IAC3D,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,WAAW,EAAE,qBAAqB,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAA;IACjH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAA;AAChC,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAgC,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;AAEvH,SAAS,WAAW,CAAC,MAAc,EAAE,IAAa;IAChD,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAA;QACzE,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAC/G,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAC7C,CAAC;IACD,OAAO,QAAQ,MAAM,EAAE,CAAA;AACzB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAAa,EAAE,SAAiB,EAAE,GAAW;IACvE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,2CAA2C,CAAA;IACvG,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,yBAAyB,CAAA;IAC7G,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,SAAS;QAAE,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,gBAAgB,SAAS,QAAQ,CAAA;IACnG,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;IAEjD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,MAAM,GAAG,GAAqB,EAAE,CAAA;IAChC,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,WAAW,EAAE,oBAAoB,CAAA;QAC5D,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAA;QACpE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,SAAS;YAAE,OAAO,WAAW,EAAE,6BAA6B,CAAA;QAC/I,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO,SAAS,KAAK,iBAAiB,CAAA;QAC3D,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACf,sEAAsE;QACtE,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,YAAY;YAAE,OAAO,WAAW,EAAE,kCAAkC,CAAA;QAC1H,MAAM,CAAC,GAAiB,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAA;QACrE,IAAI,OAAO,SAAS,KAAK,SAAS;YAAE,OAAO,WAAW,EAAE,8BAA8B,CAAA;QACtF,IAAI,CAAC,KAAK,YAAY,IAAI,SAAS;YAAE,OAAO,WAAW,EAAE,6BAA6B,CAAA;QACtF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;YAAE,OAAO,WAAW,EAAE,8BAA8B,CAAA;QAClF,IAAI,UAAU,CAAC,MAAM,GAAG,KAAK;YAAE,OAAO,WAAW,EAAE,SAAS,UAAU,CAAC,MAAM,uBAAuB,KAAK,EAAE,CAAA;QAC3G,IAAI,gBAAgB,KAAK,SAAS,IAAI,gBAAgB,KAAK,IAAI,IAAI,CAAC,OAAO,gBAAgB,KAAK,QAAQ,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;YAC3I,OAAO,WAAW,EAAE,oCAAoC,CAAA;QAC1D,CAAC;QAED,MAAM,MAAM,GAAwB,EAAE,CAAA;QACtC,KAAK,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,WAAW,EAAE,gBAAgB,CAAC,0BAA0B,CAAA;YACnJ,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;gBAAE,OAAO,WAAW,EAAE,gBAAgB,CAAC,yBAAyB,CAAA;YAClI,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;YACtC,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK;gBAAE,OAAO,WAAW,EAAE,iCAAiC,CAAA;YAC1F,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;QACzD,CAAC;QACD,IAAI,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,WAAW,EAAE,kCAAkC,CAAA;QAC5F,GAAG,CAAC,IAAI,CAAC;YACP,KAAK;YACL,IAAI,EAAE,CAAC;YACP,SAAS;YACT,UAAU,EAAE,MAAM;YAClB,GAAG,CAAC,OAAO,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtE,CAAC,CAAA;IACJ,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAA;IACrC,OAAO,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,CAAA;AAC1D,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const HEADER_SIZE = 96;
|
|
2
|
+
export interface ShardHeader {
|
|
3
|
+
readonly domain: string;
|
|
4
|
+
readonly modelVersion: string;
|
|
5
|
+
readonly indexVersion: string;
|
|
6
|
+
readonly rows: number;
|
|
7
|
+
readonly dim: number;
|
|
8
|
+
}
|
|
9
|
+
/** A loaded shard: rows as float32, ready for the dot products. */
|
|
10
|
+
export interface Shard extends ShardHeader {
|
|
11
|
+
/** rows × dim, row-major. */
|
|
12
|
+
readonly matrix: Float32Array;
|
|
13
|
+
/** Row id per row (the catalogue's card id). */
|
|
14
|
+
readonly ids: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
export declare function parseShardHeader(buffer: ArrayBuffer): ShardHeader & {
|
|
17
|
+
readonly dtype: number;
|
|
18
|
+
};
|
|
19
|
+
export declare function halfToFloat(bits: number): number;
|
|
20
|
+
export interface ShardExpectations {
|
|
21
|
+
readonly domain: string;
|
|
22
|
+
readonly modelVersion: string;
|
|
23
|
+
readonly indexVersion: string;
|
|
24
|
+
readonly rows: number;
|
|
25
|
+
readonly dim: number;
|
|
26
|
+
}
|
|
27
|
+
/** Parses and checks the binary against what the manifest promised, and decodes it to float32. */
|
|
28
|
+
export declare function decodeShard(buffer: ArrayBuffer, ids: readonly string[], expect: ShardExpectations): Shard;
|
|
29
|
+
/** The ids sidecar, `index_<domain>_v<N>.json`. */
|
|
30
|
+
export declare function parseIds(json: unknown, expect: Pick<ShardExpectations, 'domain' | 'modelVersion' | 'indexVersion' | 'rows'>): string[];
|
|
31
|
+
//# sourceMappingURL=shard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shard.d.ts","sourceRoot":"","sources":["../../src/identify/shard.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,WAAW,KAAK,CAAA;AAO7B,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAED,mEAAmE;AACnE,MAAM,WAAW,KAAM,SAAQ,WAAW;IACxC,6BAA6B;IAC7B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAC7B,gDAAgD;IAChD,QAAQ,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,CAAA;CAChC;AAmBD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAgB9F;AAmBD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAED,kGAAkG;AAClG,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,iBAAiB,GAAG,KAAK,CAsBzG;AAED,mDAAmD;AACnD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,cAAc,GAAG,cAAc,GAAG,MAAM,CAAC,GAAG,MAAM,EAAE,CAgBtI"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads an index shard, `index_<domain>_v<N>.bin`, and its ids sidecar.
|
|
3
|
+
*
|
|
4
|
+
* The binary layout is `ml/src/dejadex_ml/index_format.py`'s, a cross-language
|
|
5
|
+
* contract (change the two together):
|
|
6
|
+
*
|
|
7
|
+
* offset size field
|
|
8
|
+
* 0 4 magic "DJIX"
|
|
9
|
+
* 4 2 format_version uint16 LE (1)
|
|
10
|
+
* 6 1 dtype 1 = fp16, 2 = int8
|
|
11
|
+
* 7 1 reserved 0
|
|
12
|
+
* 8 16 domain ASCII, NUL-padded
|
|
13
|
+
* 24 32 model_version ASCII, NUL-padded
|
|
14
|
+
* 56 32 index_version ASCII, NUL-padded
|
|
15
|
+
* 88 4 rows uint32 LE
|
|
16
|
+
* 92 2 dim uint16 LE
|
|
17
|
+
* 94 2 reserved 0
|
|
18
|
+
* 96 … rows × dim fp16, row-major, rows L2-normalised
|
|
19
|
+
*
|
|
20
|
+
* Only fp16 is read: the int8 layout exists in the format, and no shard ships
|
|
21
|
+
* with it yet.
|
|
22
|
+
*/
|
|
23
|
+
import { ScannerError } from '../errors.js';
|
|
24
|
+
export const HEADER_SIZE = 96;
|
|
25
|
+
const MAGIC = 'DJIX';
|
|
26
|
+
const FORMAT_VERSION = 1;
|
|
27
|
+
const DTYPE_FP16 = 1;
|
|
28
|
+
/** Typed arrays use the platform's byte order; the file is little-endian. */
|
|
29
|
+
const LITTLE_ENDIAN = new Uint8Array(new Uint16Array([1]).buffer)[0] === 1;
|
|
30
|
+
const integrity = (message) => new ScannerError('MODEL_INTEGRITY', `index shard: ${message}`);
|
|
31
|
+
function text(bytes, offset, size, name) {
|
|
32
|
+
const raw = bytes.subarray(offset, offset + size);
|
|
33
|
+
const nul = raw.indexOf(0);
|
|
34
|
+
const end = nul === -1 ? size : nul;
|
|
35
|
+
for (let i = end; i < size; i++)
|
|
36
|
+
if (raw[i] !== 0)
|
|
37
|
+
throw integrity(`${name} has bytes after its NUL padding`);
|
|
38
|
+
let out = '';
|
|
39
|
+
for (let i = 0; i < end; i++) {
|
|
40
|
+
const ch = raw[i];
|
|
41
|
+
if (ch < 0x20 || ch > 0x7e)
|
|
42
|
+
throw integrity(`${name} is not printable ASCII`);
|
|
43
|
+
out += String.fromCharCode(ch);
|
|
44
|
+
}
|
|
45
|
+
if (out.length === 0)
|
|
46
|
+
throw integrity(`${name} is empty`);
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
export function parseShardHeader(buffer) {
|
|
50
|
+
if (buffer.byteLength < HEADER_SIZE)
|
|
51
|
+
throw integrity(`${buffer.byteLength} bytes is shorter than its header`);
|
|
52
|
+
const bytes = new Uint8Array(buffer, 0, HEADER_SIZE);
|
|
53
|
+
const view = new DataView(buffer, 0, HEADER_SIZE);
|
|
54
|
+
if (String.fromCharCode(...bytes.subarray(0, 4)) !== MAGIC)
|
|
55
|
+
throw integrity('bad magic');
|
|
56
|
+
const version = view.getUint16(4, true);
|
|
57
|
+
if (version !== FORMAT_VERSION)
|
|
58
|
+
throw integrity(`unsupported format version ${version}`);
|
|
59
|
+
if (bytes[7] !== 0 || view.getUint16(94, true) !== 0)
|
|
60
|
+
throw integrity('reserved header bytes are not zero');
|
|
61
|
+
return {
|
|
62
|
+
dtype: bytes[6],
|
|
63
|
+
domain: text(bytes, 8, 16, 'domain'),
|
|
64
|
+
modelVersion: text(bytes, 24, 32, 'model version'),
|
|
65
|
+
indexVersion: text(bytes, 56, 32, 'index version'),
|
|
66
|
+
rows: view.getUint32(88, true),
|
|
67
|
+
dim: view.getUint16(92, true),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** IEEE 754 half → float, by table (65 536 entries, built once). */
|
|
71
|
+
let HALF_TABLE = null;
|
|
72
|
+
function halfTable() {
|
|
73
|
+
if (HALF_TABLE)
|
|
74
|
+
return HALF_TABLE;
|
|
75
|
+
const t = new Float32Array(65536);
|
|
76
|
+
for (let h = 0; h < 65536; h++) {
|
|
77
|
+
const sign = h & 0x8000 ? -1 : 1;
|
|
78
|
+
const exp = (h >> 10) & 0x1f;
|
|
79
|
+
const frac = h & 0x3ff;
|
|
80
|
+
if (exp === 0)
|
|
81
|
+
t[h] = sign * frac * 2 ** -24;
|
|
82
|
+
else if (exp === 31)
|
|
83
|
+
t[h] = frac ? Number.NaN : sign * Number.POSITIVE_INFINITY;
|
|
84
|
+
else
|
|
85
|
+
t[h] = sign * (1 + frac / 1024) * 2 ** (exp - 15);
|
|
86
|
+
}
|
|
87
|
+
HALF_TABLE = t;
|
|
88
|
+
return t;
|
|
89
|
+
}
|
|
90
|
+
export function halfToFloat(bits) {
|
|
91
|
+
return halfTable()[bits & 0xffff];
|
|
92
|
+
}
|
|
93
|
+
/** Parses and checks the binary against what the manifest promised, and decodes it to float32. */
|
|
94
|
+
export function decodeShard(buffer, ids, expect) {
|
|
95
|
+
const header = parseShardHeader(buffer);
|
|
96
|
+
if (header.dtype !== DTYPE_FP16)
|
|
97
|
+
throw integrity(`dtype ${header.dtype} is not supported (fp16 only)`);
|
|
98
|
+
for (const key of ['domain', 'modelVersion', 'indexVersion', 'rows', 'dim']) {
|
|
99
|
+
if (header[key] !== expect[key]) {
|
|
100
|
+
throw integrity(`header ${key} ${String(header[key])} does not match the manifest's ${String(expect[key])}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const size = HEADER_SIZE + header.rows * header.dim * 2;
|
|
104
|
+
if (buffer.byteLength !== size)
|
|
105
|
+
throw integrity(`${buffer.byteLength} bytes, the header implies ${size}`);
|
|
106
|
+
if (ids.length !== header.rows)
|
|
107
|
+
throw integrity(`${ids.length} ids for ${header.rows} rows`);
|
|
108
|
+
const n = header.rows * header.dim;
|
|
109
|
+
const table = halfTable();
|
|
110
|
+
const matrix = new Float32Array(n);
|
|
111
|
+
if (LITTLE_ENDIAN) {
|
|
112
|
+
const halves = new Uint16Array(buffer, HEADER_SIZE, n);
|
|
113
|
+
for (let i = 0; i < n; i++)
|
|
114
|
+
matrix[i] = table[halves[i]];
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const view = new DataView(buffer, HEADER_SIZE);
|
|
118
|
+
for (let i = 0; i < n; i++)
|
|
119
|
+
matrix[i] = table[view.getUint16(i * 2, true)];
|
|
120
|
+
}
|
|
121
|
+
return { domain: header.domain, modelVersion: header.modelVersion, indexVersion: header.indexVersion, rows: header.rows, dim: header.dim, matrix, ids };
|
|
122
|
+
}
|
|
123
|
+
/** The ids sidecar, `index_<domain>_v<N>.json`. */
|
|
124
|
+
export function parseIds(json, expect) {
|
|
125
|
+
if (typeof json !== 'object' || json === null)
|
|
126
|
+
throw integrity('ids sidecar is not an object');
|
|
127
|
+
const doc = json;
|
|
128
|
+
if (doc.format !== 1)
|
|
129
|
+
throw integrity(`ids sidecar format ${String(doc.format)} is not supported`);
|
|
130
|
+
if (doc.shard !== expect.domain || doc.modelVersion !== expect.modelVersion || doc.indexVersion !== expect.indexVersion) {
|
|
131
|
+
throw integrity('ids sidecar belongs to another shard or version');
|
|
132
|
+
}
|
|
133
|
+
const ids = doc.ids;
|
|
134
|
+
if (!Array.isArray(ids) || ids.length !== expect.rows)
|
|
135
|
+
throw integrity(`ids sidecar lists ${Array.isArray(ids) ? ids.length : 'no'} ids for ${expect.rows} rows`);
|
|
136
|
+
const seen = new Set();
|
|
137
|
+
for (const id of ids) {
|
|
138
|
+
if (typeof id !== 'string' || id.length === 0)
|
|
139
|
+
throw integrity('ids sidecar has a row id that is not a string');
|
|
140
|
+
if (seen.has(id))
|
|
141
|
+
throw integrity(`row id ${id} appears twice`);
|
|
142
|
+
seen.add(id);
|
|
143
|
+
}
|
|
144
|
+
return ids;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=shard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shard.js","sourceRoot":"","sources":["../../src/identify/shard.ts"],"names":[],"mappings":"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"}
|