@camstack/addon-post-analysis 1.2.286 → 1.2.287
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{clip-model-registry-D4mC2M7F.mjs → clip-model-registry-BRHeTbFV.mjs} +36 -42
- package/dist/{clip-model-registry-CY0FGcpQ.js → clip-model-registry-Cq1X8kTo.js} +36 -42
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +2 -2
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CkfvSztj.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BpJC8P0K.mjs} +2 -2
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-NKbCrEkH.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-3dZf67Z7.mjs} +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-r4OU_ahc.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-CelYj4QN.mjs → hostInit-BIUCfS5T.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +605 -62
- package/dist/pipeline-analytics/index.mjs +605 -62
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BAYoLHsN.mjs +0 -26
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_clip_model_registry = require("../clip-model-registry-
|
|
5
|
+
const require_clip_model_registry = require("../clip-model-registry-Cq1X8kTo.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_clip_model_registry.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -88,7 +88,7 @@ function motionEpisodeDurationMs(state) {
|
|
|
88
88
|
}
|
|
89
89
|
//#endregion
|
|
90
90
|
//#region src/pipeline-analytics/motion-episode-tracker.ts
|
|
91
|
-
function keyOf$
|
|
91
|
+
function keyOf$2(deviceId, source) {
|
|
92
92
|
return `${deviceId}:${source}`;
|
|
93
93
|
}
|
|
94
94
|
var MotionEpisodeTracker = class {
|
|
@@ -104,7 +104,7 @@ var MotionEpisodeTracker = class {
|
|
|
104
104
|
* {@link MotionEpisodeInsertInput}.
|
|
105
105
|
*/
|
|
106
106
|
async observe(deviceId, source, detected, timestamp, regionData) {
|
|
107
|
-
const key = keyOf$
|
|
107
|
+
const key = keyOf$2(deviceId, source);
|
|
108
108
|
const prior = this.signals.get(key);
|
|
109
109
|
const decision = decideMotionSignal(prior, detected, timestamp);
|
|
110
110
|
switch (decision.kind) {
|
|
@@ -12780,14 +12780,14 @@ var NcPruneRecordSchema = require_clip_model_registry.object({
|
|
|
12780
12780
|
});
|
|
12781
12781
|
/** Rules × deleted subjects — small; a generous ceiling. */
|
|
12782
12782
|
var LOAD_LIMIT$4 = 1e4;
|
|
12783
|
-
function keyOf(row) {
|
|
12783
|
+
function keyOf$1(row) {
|
|
12784
12784
|
return `${row.kind}:${row.ruleId}:${row.field}:${row.subject}`;
|
|
12785
12785
|
}
|
|
12786
12786
|
var NC_DEVICE_PRUNES_SPEC = {
|
|
12787
12787
|
collection: NC_DEVICE_PRUNES_COLLECTION,
|
|
12788
12788
|
columns: [...NC_DEVICE_PRUNES_COLUMNS],
|
|
12789
12789
|
writeMode: "write-through",
|
|
12790
|
-
keyOf,
|
|
12790
|
+
keyOf: keyOf$1,
|
|
12791
12791
|
toValue: (row) => ({
|
|
12792
12792
|
ruleId: row.ruleId,
|
|
12793
12793
|
at: row.at,
|
|
@@ -12827,7 +12827,7 @@ var NcPruneLedger = class {
|
|
|
12827
12827
|
* {@link isLoaded} as it was — see the module docblock.
|
|
12828
12828
|
*/
|
|
12829
12829
|
async load() {
|
|
12830
|
-
const heldBefore = new Set(this.ledger.snapshot().map(keyOf));
|
|
12830
|
+
const heldBefore = new Set(this.ledger.snapshot().map(keyOf$1));
|
|
12831
12831
|
const startedAt = this.writes;
|
|
12832
12832
|
const writtenSince = (key) => (this.writtenAt.get(key) ?? 0) > startedAt;
|
|
12833
12833
|
try {
|
|
@@ -12838,7 +12838,7 @@ var NcPruneLedger = class {
|
|
|
12838
12838
|
const next = /* @__PURE__ */ new Map();
|
|
12839
12839
|
for (const record of records) {
|
|
12840
12840
|
const row = NC_DEVICE_PRUNES_SPEC.fromRecord(record.id, record.data);
|
|
12841
|
-
if (row !== null) next.set(keyOf(row), row);
|
|
12841
|
+
if (row !== null) next.set(keyOf$1(row), row);
|
|
12842
12842
|
}
|
|
12843
12843
|
for (const key of heldBefore) if (!next.has(key) && !writtenSince(key)) this.ledger.evict(key);
|
|
12844
12844
|
for (const [key, row] of next) if (!writtenSince(key)) this.ledger.stage(row);
|
|
@@ -12868,7 +12868,7 @@ var NcPruneLedger = class {
|
|
|
12868
12868
|
*/
|
|
12869
12869
|
async record(notes, source, at) {
|
|
12870
12870
|
for (const note of notes) {
|
|
12871
|
-
const held = this.ledger.get(keyOf(note));
|
|
12871
|
+
const held = this.ledger.get(keyOf$1(note));
|
|
12872
12872
|
if (held !== void 0 && KEPT_OUTCOMES.has(held.outcome) && !KEPT_OUTCOMES.has(note.outcome)) continue;
|
|
12873
12873
|
const row = NcPruneRecordSchema.parse({
|
|
12874
12874
|
...note,
|
|
@@ -12876,7 +12876,7 @@ var NcPruneLedger = class {
|
|
|
12876
12876
|
source
|
|
12877
12877
|
});
|
|
12878
12878
|
this.writes += 1;
|
|
12879
|
-
this.writtenAt.set(keyOf(row), this.writes);
|
|
12879
|
+
this.writtenAt.set(keyOf$1(row), this.writes);
|
|
12880
12880
|
await this.ledger.put(row);
|
|
12881
12881
|
}
|
|
12882
12882
|
}
|
|
@@ -39708,6 +39708,87 @@ var ReembedFacesInputSchema = require_clip_model_registry.object({
|
|
|
39708
39708
|
/** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
|
|
39709
39709
|
executeOnNodeId: require_clip_model_registry.string().optional()
|
|
39710
39710
|
});
|
|
39711
|
+
/**
|
|
39712
|
+
* D658: re-embed each sample's stored template with ONE pinned model and
|
|
39713
|
+
* compare with the stored vector — does the stamp say what produced it?
|
|
39714
|
+
* Read-only: it reports and writes nothing.
|
|
39715
|
+
*/
|
|
39716
|
+
var AuditFaceEmbeddingsInputSchema = require_clip_model_registry.object({
|
|
39717
|
+
/** The stamp to verify. Omit for `auraface-r100` (the D651 default). */
|
|
39718
|
+
modelId: require_clip_model_registry.string().min(1).optional(),
|
|
39719
|
+
/** Only this identity's samples. */
|
|
39720
|
+
identityId: require_clip_model_registry.string().min(1).optional(),
|
|
39721
|
+
/** Stop after this many measured samples. */
|
|
39722
|
+
maxSamples: require_clip_model_registry.number().int().positive().optional(),
|
|
39723
|
+
/** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
|
|
39724
|
+
executeOnNodeId: require_clip_model_registry.string().optional()
|
|
39725
|
+
});
|
|
39726
|
+
var AuditFaceEmbeddingsResultSchema = require_clip_model_registry.object({
|
|
39727
|
+
auditedModelId: require_clip_model_registry.string(),
|
|
39728
|
+
total: require_clip_model_registry.number(),
|
|
39729
|
+
ok: require_clip_model_registry.number(),
|
|
39730
|
+
mismatch: require_clip_model_registry.number(),
|
|
39731
|
+
inconclusive: require_clip_model_registry.number(),
|
|
39732
|
+
unmeasurable: require_clip_model_registry.number(),
|
|
39733
|
+
complete: require_clip_model_registry.boolean(),
|
|
39734
|
+
okMinCosine: require_clip_model_registry.number(),
|
|
39735
|
+
mismatchMaxCosine: require_clip_model_registry.number(),
|
|
39736
|
+
rows: require_clip_model_registry.array(require_clip_model_registry.object({
|
|
39737
|
+
sampleId: require_clip_model_registry.string(),
|
|
39738
|
+
identityId: require_clip_model_registry.string(),
|
|
39739
|
+
identityName: require_clip_model_registry.string(),
|
|
39740
|
+
stampedModelId: require_clip_model_registry.string(),
|
|
39741
|
+
verdict: require_clip_model_registry._enum([
|
|
39742
|
+
"ok",
|
|
39743
|
+
"mismatch",
|
|
39744
|
+
"inconclusive",
|
|
39745
|
+
"unmeasurable"
|
|
39746
|
+
]),
|
|
39747
|
+
cosine: require_clip_model_registry.number().optional(),
|
|
39748
|
+
reason: require_clip_model_registry._enum([
|
|
39749
|
+
"stamped-other-model",
|
|
39750
|
+
"no-pixels",
|
|
39751
|
+
"no-stored-vector",
|
|
39752
|
+
"embed-failed",
|
|
39753
|
+
"no-capable-node",
|
|
39754
|
+
"dimension-differs"
|
|
39755
|
+
]).optional(),
|
|
39756
|
+
detail: require_clip_model_registry.string().optional()
|
|
39757
|
+
}))
|
|
39758
|
+
});
|
|
39759
|
+
/**
|
|
39760
|
+
* D658: re-embed EXACTLY these samples with ONE pinned model, replacing their
|
|
39761
|
+
* vectors even when already stamped with it — the repair for what
|
|
39762
|
+
* `faceGallery.auditEmbeddings` accuses. See `face-sample-reembed.ts`.
|
|
39763
|
+
*/
|
|
39764
|
+
var ReembedSamplesInputSchema = require_clip_model_registry.object({
|
|
39765
|
+
sampleIds: require_clip_model_registry.array(require_clip_model_registry.string().min(1)).min(1).max(200),
|
|
39766
|
+
modelId: require_clip_model_registry.string().min(1),
|
|
39767
|
+
/** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
|
|
39768
|
+
executeOnNodeId: require_clip_model_registry.string().optional()
|
|
39769
|
+
});
|
|
39770
|
+
var ReembedSamplesResultSchema = require_clip_model_registry.object({
|
|
39771
|
+
pinnedModelId: require_clip_model_registry.string(),
|
|
39772
|
+
requested: require_clip_model_registry.number(),
|
|
39773
|
+
reembedded: require_clip_model_registry.number(),
|
|
39774
|
+
failed: require_clip_model_registry.number(),
|
|
39775
|
+
rows: require_clip_model_registry.array(require_clip_model_registry.object({
|
|
39776
|
+
sampleId: require_clip_model_registry.string(),
|
|
39777
|
+
outcome: require_clip_model_registry._enum([
|
|
39778
|
+
"reembedded",
|
|
39779
|
+
"not-found",
|
|
39780
|
+
"duplicate",
|
|
39781
|
+
"no-pixels",
|
|
39782
|
+
"embed-failed",
|
|
39783
|
+
"no-capable-node",
|
|
39784
|
+
"no-model-stamp",
|
|
39785
|
+
"not-landed"
|
|
39786
|
+
]),
|
|
39787
|
+
previousModelId: require_clip_model_registry.string().optional(),
|
|
39788
|
+
modelId: require_clip_model_registry.string().optional(),
|
|
39789
|
+
detail: require_clip_model_registry.string().optional()
|
|
39790
|
+
}))
|
|
39791
|
+
});
|
|
39711
39792
|
/** Mirrors `FaceReembedResult` (`face-reembed-pass.ts`) minus the sample-id set. */
|
|
39712
39793
|
var ReembedFacesResultSchema = require_clip_model_registry.object({
|
|
39713
39794
|
targetModelId: require_clip_model_registry.string(),
|
|
@@ -39959,11 +40040,256 @@ var embeddingActions = require_clip_model_registry.defineCustomActions({
|
|
|
39959
40040
|
"embedding.faceAlignmentStatus": require_clip_model_registry.customAction(NoInputSchema$1, FaceAlignmentStatusSchema),
|
|
39960
40041
|
"embedding.backfillPhotoSources": require_clip_model_registry.customAction(BackfillPhotoSourcesInputSchema, BackfillPhotoSourcesResultSchema, { auth: "admin" }),
|
|
39961
40042
|
"faceGallery.sourceFrameStats": require_clip_model_registry.customAction(NoInputSchema$1, SourceFrameStatsSchema),
|
|
40043
|
+
"faceGallery.reembedSamples": require_clip_model_registry.customAction(ReembedSamplesInputSchema, ReembedSamplesResultSchema, {
|
|
40044
|
+
kind: "mutation",
|
|
40045
|
+
auth: "admin"
|
|
40046
|
+
}),
|
|
40047
|
+
"faceGallery.auditEmbeddings": require_clip_model_registry.customAction(AuditFaceEmbeddingsInputSchema, AuditFaceEmbeddingsResultSchema, { auth: "admin" }),
|
|
39962
40048
|
"faceGallery.resetToCore": require_clip_model_registry.customAction(ResetToCoreInputSchema, ResetToCoreResultSchema, {
|
|
39963
40049
|
kind: "mutation",
|
|
39964
40050
|
auth: "admin"
|
|
39965
40051
|
})
|
|
39966
40052
|
});
|
|
40053
|
+
async function reembedOne(deps, sample, pinned) {
|
|
40054
|
+
const base = {
|
|
40055
|
+
sampleId: sample.sampleId,
|
|
40056
|
+
previousModelId: sample.modelId
|
|
40057
|
+
};
|
|
40058
|
+
const crop = await deps.loadAlignedCrop(sample);
|
|
40059
|
+
if (crop === null) return {
|
|
40060
|
+
...base,
|
|
40061
|
+
outcome: "no-pixels"
|
|
40062
|
+
};
|
|
40063
|
+
const outcome = await deps.embed(sample, crop);
|
|
40064
|
+
if (outcome.kind === "failed") return {
|
|
40065
|
+
...base,
|
|
40066
|
+
outcome: "embed-failed",
|
|
40067
|
+
detail: outcome.reason
|
|
40068
|
+
};
|
|
40069
|
+
if (outcome.kind === "no-capable-node") return {
|
|
40070
|
+
...base,
|
|
40071
|
+
outcome: "no-capable-node",
|
|
40072
|
+
detail: outcome.reason
|
|
40073
|
+
};
|
|
40074
|
+
if (outcome.modelId === void 0 || outcome.modelId === "") return {
|
|
40075
|
+
...base,
|
|
40076
|
+
outcome: "no-model-stamp",
|
|
40077
|
+
detail: "the embed answered no model id"
|
|
40078
|
+
};
|
|
40079
|
+
if (outcome.modelId !== pinned) return {
|
|
40080
|
+
...base,
|
|
40081
|
+
outcome: "embed-failed",
|
|
40082
|
+
detail: `ran "${outcome.modelId}" for a call pinned to "${pinned}"`
|
|
40083
|
+
};
|
|
40084
|
+
const write = await deps.store(sample, outcome.embedding, outcome.modelId, outcome.dim);
|
|
40085
|
+
if (!write.landed) return {
|
|
40086
|
+
...base,
|
|
40087
|
+
outcome: "not-landed",
|
|
40088
|
+
detail: write.reason ?? "the write did not land"
|
|
40089
|
+
};
|
|
40090
|
+
return {
|
|
40091
|
+
...base,
|
|
40092
|
+
outcome: "reembedded",
|
|
40093
|
+
modelId: outcome.modelId
|
|
40094
|
+
};
|
|
40095
|
+
}
|
|
40096
|
+
async function reembedFaceSamples(deps, options) {
|
|
40097
|
+
const pinned = options.modelId;
|
|
40098
|
+
const byId = new Map((await deps.listSamples()).map((s) => [s.sampleId, s]));
|
|
40099
|
+
const pacingMs = options.pacingMs ?? 25;
|
|
40100
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40101
|
+
const rows = [];
|
|
40102
|
+
let noNode = null;
|
|
40103
|
+
deps.logger.info("face sample re-embed starting", { meta: {
|
|
40104
|
+
pinnedModelId: pinned,
|
|
40105
|
+
requested: options.sampleIds.length
|
|
40106
|
+
} });
|
|
40107
|
+
for (const sampleId of options.sampleIds) {
|
|
40108
|
+
if (seen.has(sampleId)) {
|
|
40109
|
+
rows.push({
|
|
40110
|
+
sampleId,
|
|
40111
|
+
outcome: "duplicate"
|
|
40112
|
+
});
|
|
40113
|
+
continue;
|
|
40114
|
+
}
|
|
40115
|
+
seen.add(sampleId);
|
|
40116
|
+
const sample = byId.get(sampleId);
|
|
40117
|
+
if (sample === void 0) {
|
|
40118
|
+
rows.push({
|
|
40119
|
+
sampleId,
|
|
40120
|
+
outcome: "not-found"
|
|
40121
|
+
});
|
|
40122
|
+
continue;
|
|
40123
|
+
}
|
|
40124
|
+
if (noNode !== null) {
|
|
40125
|
+
rows.push({
|
|
40126
|
+
sampleId,
|
|
40127
|
+
previousModelId: sample.modelId,
|
|
40128
|
+
outcome: "no-capable-node",
|
|
40129
|
+
detail: noNode
|
|
40130
|
+
});
|
|
40131
|
+
continue;
|
|
40132
|
+
}
|
|
40133
|
+
const row = await reembedOne(deps, sample, pinned);
|
|
40134
|
+
if (row.outcome === "no-capable-node") noNode = row.detail ?? "no capable node";
|
|
40135
|
+
if (row.outcome !== "reembedded") deps.logger.warn("face sample re-embed: sample NOT re-embedded", { meta: {
|
|
40136
|
+
sampleId,
|
|
40137
|
+
outcome: row.outcome,
|
|
40138
|
+
detail: row.detail ?? null,
|
|
40139
|
+
pinnedModelId: pinned
|
|
40140
|
+
} });
|
|
40141
|
+
rows.push(row);
|
|
40142
|
+
await deps.pause(pacingMs);
|
|
40143
|
+
}
|
|
40144
|
+
const reembedded = rows.filter((r) => r.outcome === "reembedded").length;
|
|
40145
|
+
deps.logger.info("face sample re-embed finished", { meta: {
|
|
40146
|
+
pinnedModelId: pinned,
|
|
40147
|
+
requested: options.sampleIds.length,
|
|
40148
|
+
reembedded,
|
|
40149
|
+
failed: rows.length - reembedded
|
|
40150
|
+
} });
|
|
40151
|
+
return {
|
|
40152
|
+
pinnedModelId: pinned,
|
|
40153
|
+
requested: options.sampleIds.length,
|
|
40154
|
+
reembedded,
|
|
40155
|
+
failed: rows.length - reembedded,
|
|
40156
|
+
rows
|
|
40157
|
+
};
|
|
40158
|
+
}
|
|
40159
|
+
//#endregion
|
|
40160
|
+
//#region src/pipeline-analytics/face-embedding-audit.ts
|
|
40161
|
+
/** At or above: the stored vector is this model's. */
|
|
40162
|
+
var AUDIT_OK_MIN_COSINE = .98;
|
|
40163
|
+
/** Below: the stored vector is NOT this model's. Between the two: inconclusive. */
|
|
40164
|
+
var AUDIT_MISMATCH_MAX_COSINE = .9;
|
|
40165
|
+
/** Cosine of two vectors of equal length; `null` when either has no length. */
|
|
40166
|
+
function cosine(a, b) {
|
|
40167
|
+
let dot = 0;
|
|
40168
|
+
let na = 0;
|
|
40169
|
+
let nb = 0;
|
|
40170
|
+
for (let i = 0; i < a.length; i++) {
|
|
40171
|
+
const x = a[i] ?? 0;
|
|
40172
|
+
const y = b[i] ?? 0;
|
|
40173
|
+
dot += x * y;
|
|
40174
|
+
na += x * x;
|
|
40175
|
+
nb += y * y;
|
|
40176
|
+
}
|
|
40177
|
+
if (na === 0 || nb === 0) return null;
|
|
40178
|
+
return dot / Math.sqrt(na * nb);
|
|
40179
|
+
}
|
|
40180
|
+
/** The verdict a measured cosine earns. Pure. */
|
|
40181
|
+
function verdictOf(c) {
|
|
40182
|
+
if (c >= .98) return "ok";
|
|
40183
|
+
if (c < .9) return "mismatch";
|
|
40184
|
+
return "inconclusive";
|
|
40185
|
+
}
|
|
40186
|
+
function rowBase(s) {
|
|
40187
|
+
return {
|
|
40188
|
+
sampleId: s.sampleId,
|
|
40189
|
+
identityId: s.identityId,
|
|
40190
|
+
identityName: s.identityName,
|
|
40191
|
+
stampedModelId: s.modelId
|
|
40192
|
+
};
|
|
40193
|
+
}
|
|
40194
|
+
async function measure$2(deps, sample, stored) {
|
|
40195
|
+
const base = rowBase(sample);
|
|
40196
|
+
if (stored === void 0) return {
|
|
40197
|
+
...base,
|
|
40198
|
+
verdict: "unmeasurable",
|
|
40199
|
+
reason: "no-stored-vector"
|
|
40200
|
+
};
|
|
40201
|
+
const crop = await deps.loadAlignedCrop(sample);
|
|
40202
|
+
if (crop === null) return {
|
|
40203
|
+
...base,
|
|
40204
|
+
verdict: "unmeasurable",
|
|
40205
|
+
reason: "no-pixels"
|
|
40206
|
+
};
|
|
40207
|
+
const outcome = await deps.embed(sample, crop);
|
|
40208
|
+
if (outcome.kind === "failed") return {
|
|
40209
|
+
...base,
|
|
40210
|
+
verdict: "unmeasurable",
|
|
40211
|
+
reason: "embed-failed",
|
|
40212
|
+
detail: outcome.reason
|
|
40213
|
+
};
|
|
40214
|
+
if (outcome.kind === "no-capable-node") return {
|
|
40215
|
+
...base,
|
|
40216
|
+
verdict: "unmeasurable",
|
|
40217
|
+
reason: "no-capable-node",
|
|
40218
|
+
detail: outcome.reason
|
|
40219
|
+
};
|
|
40220
|
+
if (outcome.embedding.length !== stored.length) return {
|
|
40221
|
+
...base,
|
|
40222
|
+
verdict: "unmeasurable",
|
|
40223
|
+
reason: "dimension-differs",
|
|
40224
|
+
detail: `stored ${stored.length}-d, re-embedded ${outcome.embedding.length}-d`
|
|
40225
|
+
};
|
|
40226
|
+
const c = cosine(outcome.embedding, stored);
|
|
40227
|
+
if (c === null) return {
|
|
40228
|
+
...base,
|
|
40229
|
+
verdict: "unmeasurable",
|
|
40230
|
+
reason: "no-stored-vector",
|
|
40231
|
+
detail: "zero vector"
|
|
40232
|
+
};
|
|
40233
|
+
return {
|
|
40234
|
+
...base,
|
|
40235
|
+
verdict: verdictOf(c),
|
|
40236
|
+
cosine: Math.round(c * 1e4) / 1e4
|
|
40237
|
+
};
|
|
40238
|
+
}
|
|
40239
|
+
/**
|
|
40240
|
+
* Audit every sample stamped with `auditedModelId`. Read-only; bounded by
|
|
40241
|
+
* `maxSamples`; a pass-terminal `no-capable-node` stops measuring and reports
|
|
40242
|
+
* the rest as unmeasurable rather than asking again per sample.
|
|
40243
|
+
*/
|
|
40244
|
+
async function auditFaceEmbeddings(deps, options) {
|
|
40245
|
+
const all = await deps.listSamples();
|
|
40246
|
+
const samples = options.identityId === void 0 ? all : all.filter((s) => s.identityId === options.identityId);
|
|
40247
|
+
const inSpace = samples.filter((s) => s.modelId === options.auditedModelId);
|
|
40248
|
+
const stored = await deps.loadStoredVectors(inSpace.map((s) => s.sampleId));
|
|
40249
|
+
const budget = options.maxSamples ?? Number.POSITIVE_INFINITY;
|
|
40250
|
+
const rows = [];
|
|
40251
|
+
let measured = 0;
|
|
40252
|
+
let complete = true;
|
|
40253
|
+
let noNode = null;
|
|
40254
|
+
for (const sample of samples) {
|
|
40255
|
+
if (sample.modelId !== options.auditedModelId) {
|
|
40256
|
+
rows.push({
|
|
40257
|
+
...rowBase(sample),
|
|
40258
|
+
verdict: "unmeasurable",
|
|
40259
|
+
reason: "stamped-other-model"
|
|
40260
|
+
});
|
|
40261
|
+
continue;
|
|
40262
|
+
}
|
|
40263
|
+
if (measured >= budget) {
|
|
40264
|
+
complete = false;
|
|
40265
|
+
continue;
|
|
40266
|
+
}
|
|
40267
|
+
if (noNode !== null) {
|
|
40268
|
+
rows.push({
|
|
40269
|
+
...rowBase(sample),
|
|
40270
|
+
verdict: "unmeasurable",
|
|
40271
|
+
reason: "no-capable-node",
|
|
40272
|
+
detail: noNode
|
|
40273
|
+
});
|
|
40274
|
+
continue;
|
|
40275
|
+
}
|
|
40276
|
+
const row = await measure$2(deps, sample, stored.get(sample.sampleId));
|
|
40277
|
+
if (row.reason === "no-capable-node") noNode = row.detail ?? "no capable node";
|
|
40278
|
+
if (row.cosine !== void 0) measured += 1;
|
|
40279
|
+
rows.push(row);
|
|
40280
|
+
}
|
|
40281
|
+
const count = (v) => rows.filter((r) => r.verdict === v).length;
|
|
40282
|
+
return {
|
|
40283
|
+
auditedModelId: options.auditedModelId,
|
|
40284
|
+
total: samples.length,
|
|
40285
|
+
ok: count("ok"),
|
|
40286
|
+
mismatch: count("mismatch"),
|
|
40287
|
+
inconclusive: count("inconclusive"),
|
|
40288
|
+
unmeasurable: count("unmeasurable"),
|
|
40289
|
+
complete,
|
|
40290
|
+
rows
|
|
40291
|
+
};
|
|
40292
|
+
}
|
|
39967
40293
|
//#endregion
|
|
39968
40294
|
//#region src/pipeline-analytics/face-alignment-version.ts
|
|
39969
40295
|
/**
|
|
@@ -66007,7 +66333,8 @@ var FaceReembedEmbedder = class {
|
|
|
66007
66333
|
return {
|
|
66008
66334
|
kind: "embedded",
|
|
66009
66335
|
embedding: vector,
|
|
66010
|
-
dim: vector.length
|
|
66336
|
+
dim: vector.length,
|
|
66337
|
+
modelId: result.modelId
|
|
66011
66338
|
};
|
|
66012
66339
|
}
|
|
66013
66340
|
}
|
|
@@ -84298,6 +84625,79 @@ async function resolveSubjectShot(ports, trackId, deviceId, aroundMs) {
|
|
|
84298
84625
|
frameHeight: dims.h
|
|
84299
84626
|
};
|
|
84300
84627
|
}
|
|
84628
|
+
var MESSAGES = {
|
|
84629
|
+
"dimension-mismatch": "object embeddings refused — their length is not their model's dimension",
|
|
84630
|
+
"no-clip-metadata": "object embeddings refused — the model has no CLIP metadata",
|
|
84631
|
+
"no-model-id": "object embeddings refused — the result names no model"
|
|
84632
|
+
};
|
|
84633
|
+
function keyOf(r) {
|
|
84634
|
+
return `${String(r.deviceId)}|${r.modelId ?? ""}|${r.reason}`;
|
|
84635
|
+
}
|
|
84636
|
+
var EmbeddingRefusalReport = class {
|
|
84637
|
+
logger;
|
|
84638
|
+
intervalMs;
|
|
84639
|
+
now;
|
|
84640
|
+
keys = /* @__PURE__ */ new Map();
|
|
84641
|
+
constructor(options) {
|
|
84642
|
+
this.logger = options.logger;
|
|
84643
|
+
this.intervalMs = options.intervalMs ?? 3e5;
|
|
84644
|
+
this.now = options.now ?? (() => Date.now());
|
|
84645
|
+
}
|
|
84646
|
+
/** Count one refusal; write a line if this key is new or its interval passed. */
|
|
84647
|
+
note(refusal) {
|
|
84648
|
+
const key = keyOf(refusal);
|
|
84649
|
+
const at = this.now();
|
|
84650
|
+
const held = this.keys.get(key);
|
|
84651
|
+
const pending = (held?.pending ?? 0) + 1;
|
|
84652
|
+
if (held === void 0 || at - held.lastLineAtMs >= this.intervalMs) {
|
|
84653
|
+
this.write(refusal, pending, held === void 0 ? "first" : "interval");
|
|
84654
|
+
this.keys = new Map(this.keys).set(key, {
|
|
84655
|
+
last: refusal,
|
|
84656
|
+
pending: 0,
|
|
84657
|
+
lastLineAtMs: at
|
|
84658
|
+
});
|
|
84659
|
+
return;
|
|
84660
|
+
}
|
|
84661
|
+
this.keys = new Map(this.keys).set(key, {
|
|
84662
|
+
...held,
|
|
84663
|
+
last: refusal,
|
|
84664
|
+
pending
|
|
84665
|
+
});
|
|
84666
|
+
}
|
|
84667
|
+
/** Report every key whose interval passed with refusals still pending. */
|
|
84668
|
+
sweep() {
|
|
84669
|
+
const at = this.now();
|
|
84670
|
+
for (const [key, held] of this.keys) {
|
|
84671
|
+
if (held.pending === 0 || at - held.lastLineAtMs < this.intervalMs) continue;
|
|
84672
|
+
this.write(held.last, held.pending, "interval");
|
|
84673
|
+
this.keys = new Map(this.keys).set(key, {
|
|
84674
|
+
...held,
|
|
84675
|
+
pending: 0,
|
|
84676
|
+
lastLineAtMs: at
|
|
84677
|
+
});
|
|
84678
|
+
}
|
|
84679
|
+
}
|
|
84680
|
+
/** Report every pending count now, whatever its interval (shutdown). */
|
|
84681
|
+
flush() {
|
|
84682
|
+
for (const held of this.keys.values()) if (held.pending > 0) this.write(held.last, held.pending, "flush");
|
|
84683
|
+
this.keys = /* @__PURE__ */ new Map();
|
|
84684
|
+
}
|
|
84685
|
+
write(r, refused, closing) {
|
|
84686
|
+
this.logger.warn(MESSAGES[r.reason], {
|
|
84687
|
+
tags: { deviceId: r.deviceId },
|
|
84688
|
+
meta: {
|
|
84689
|
+
reason: r.reason,
|
|
84690
|
+
modelId: r.modelId,
|
|
84691
|
+
refused,
|
|
84692
|
+
windowMs: this.intervalMs,
|
|
84693
|
+
closing,
|
|
84694
|
+
lastTrackId: r.trackId,
|
|
84695
|
+
...r.dim !== void 0 ? { dim: r.dim } : {},
|
|
84696
|
+
...r.expected !== void 0 ? { expected: r.expected } : {}
|
|
84697
|
+
}
|
|
84698
|
+
});
|
|
84699
|
+
}
|
|
84700
|
+
};
|
|
84301
84701
|
//#endregion
|
|
84302
84702
|
//#region src/pipeline-analytics/store/object-embedding-store.ts
|
|
84303
84703
|
/**
|
|
@@ -84360,10 +84760,33 @@ var ObjectEmbeddingStore = class {
|
|
|
84360
84760
|
clipModels;
|
|
84361
84761
|
/** index → the dim this process declared it at. Per index, per process. */
|
|
84362
84762
|
declaredDims = /* @__PURE__ */ new Map();
|
|
84763
|
+
/**
|
|
84764
|
+
* Refused writes, COUNTED per (camera, model, reason). A refusal is systemic
|
|
84765
|
+
* — a runner stamping the wrong model refuses every vector it sends — so a
|
|
84766
|
+
* per-track WARN was a flood that hid the one fact that mattered (D658).
|
|
84767
|
+
*/
|
|
84768
|
+
refusals;
|
|
84363
84769
|
constructor(deps) {
|
|
84364
84770
|
this.vectorStore = deps.vectorStore;
|
|
84365
84771
|
this.logger = deps.logger;
|
|
84366
84772
|
this.clipModels = deps.clipModels;
|
|
84773
|
+
this.refusals = new EmbeddingRefusalReport({
|
|
84774
|
+
logger: deps.logger,
|
|
84775
|
+
...deps.refusalReportIntervalMs !== void 0 ? { intervalMs: deps.refusalReportIntervalMs } : {},
|
|
84776
|
+
...deps.now !== void 0 ? { now: deps.now } : {}
|
|
84777
|
+
});
|
|
84778
|
+
}
|
|
84779
|
+
/**
|
|
84780
|
+
* Count a write refused BEFORE it reached this store — a detail whose result
|
|
84781
|
+
* named no model (D658). Same report, same rate limit, as the refusals the
|
|
84782
|
+
* store makes itself.
|
|
84783
|
+
*/
|
|
84784
|
+
noteRefused(refusal) {
|
|
84785
|
+
this.refusals.note(refusal);
|
|
84786
|
+
}
|
|
84787
|
+
/** Report every refusal count still pending (shutdown). */
|
|
84788
|
+
flushRefusals() {
|
|
84789
|
+
this.refusals.flush();
|
|
84367
84790
|
}
|
|
84368
84791
|
/** Every index a delete, scan or wipe must reach. */
|
|
84369
84792
|
indexes() {
|
|
@@ -84461,19 +84884,28 @@ var ObjectEmbeddingStore = class {
|
|
|
84461
84884
|
* and dropped", because a counter that cannot reads as verification.
|
|
84462
84885
|
*/
|
|
84463
84886
|
async upsertIfBetter(input) {
|
|
84887
|
+
this.refusals.sweep();
|
|
84464
84888
|
const vector = resolveVectorPayload(input);
|
|
84465
84889
|
if (vector === null) return "failed";
|
|
84466
|
-
const space = this.
|
|
84467
|
-
if (space === null)
|
|
84890
|
+
const space = this.clipModels.spaceFor(input.modelId);
|
|
84891
|
+
if (space === null) {
|
|
84892
|
+
this.refusals.note({
|
|
84893
|
+
deviceId: input.deviceId,
|
|
84894
|
+
modelId: input.modelId,
|
|
84895
|
+
reason: "no-clip-metadata",
|
|
84896
|
+
trackId: input.trackId,
|
|
84897
|
+
dim: vector.dim
|
|
84898
|
+
});
|
|
84899
|
+
return "failed";
|
|
84900
|
+
}
|
|
84468
84901
|
if (vector.dim !== space.dim) {
|
|
84469
|
-
this.
|
|
84470
|
-
|
|
84471
|
-
|
|
84472
|
-
|
|
84473
|
-
|
|
84474
|
-
|
|
84475
|
-
|
|
84476
|
-
}
|
|
84902
|
+
this.refusals.note({
|
|
84903
|
+
deviceId: input.deviceId,
|
|
84904
|
+
modelId: input.modelId,
|
|
84905
|
+
reason: "dimension-mismatch",
|
|
84906
|
+
trackId: input.trackId,
|
|
84907
|
+
dim: vector.dim,
|
|
84908
|
+
expected: space.dim
|
|
84477
84909
|
});
|
|
84478
84910
|
return "failed";
|
|
84479
84911
|
}
|
|
@@ -84947,6 +85379,30 @@ function rowFromMetadata(id, metadata) {
|
|
|
84947
85379
|
};
|
|
84948
85380
|
}
|
|
84949
85381
|
//#endregion
|
|
85382
|
+
//#region src/pipeline-analytics/clip-detail-route.ts
|
|
85383
|
+
async function routeClipDetailToStore(store, input) {
|
|
85384
|
+
const modelId = input.embeddingModelId;
|
|
85385
|
+
if (modelId === void 0 || modelId === "") {
|
|
85386
|
+
store.noteRefused({
|
|
85387
|
+
deviceId: input.deviceId,
|
|
85388
|
+
modelId: null,
|
|
85389
|
+
reason: "no-model-id",
|
|
85390
|
+
trackId: input.trackId
|
|
85391
|
+
});
|
|
85392
|
+
return "failed";
|
|
85393
|
+
}
|
|
85394
|
+
return store.upsertIfBetter({
|
|
85395
|
+
trackId: input.trackId,
|
|
85396
|
+
deviceId: input.deviceId,
|
|
85397
|
+
timestamp: input.timestamp,
|
|
85398
|
+
className: input.className,
|
|
85399
|
+
embeddingBase64: input.embeddingBase64,
|
|
85400
|
+
modelId,
|
|
85401
|
+
confidence: input.score,
|
|
85402
|
+
...input.keyFrameMediaId !== void 0 ? { keyFrameMediaId: input.keyFrameMediaId } : {}
|
|
85403
|
+
});
|
|
85404
|
+
}
|
|
85405
|
+
//#endregion
|
|
84950
85406
|
//#region src/pipeline-analytics/clip-space-plan.ts
|
|
84951
85407
|
/**
|
|
84952
85408
|
* Which CLIP feature space a search reads and a rebuild fills — decided in one
|
|
@@ -87158,9 +87614,6 @@ var KEYFRAME_CROP_MIN_SUBJECT_IOU = .5;
|
|
|
87158
87614
|
* covers a healthy trail many times over; past it the honest answer is "no
|
|
87159
87615
|
* evidence" and the temporal bound takes over. */
|
|
87160
87616
|
var KEYFRAME_CROP_MAX_SAMPLE_GAP_MS = 1e3;
|
|
87161
|
-
/** How long the active CLIP model id (from the embedding-encoder) is cached
|
|
87162
|
-
* before re-reading. */
|
|
87163
|
-
var CLIP_MODEL_ID_CACHE_TTL_MS = 6e4;
|
|
87164
87617
|
var SETTINGS_CACHE_TTL_MS = 5e3;
|
|
87165
87618
|
/** TTL for the per-node confirmation-gate detector-step cache (the node's
|
|
87166
87619
|
* default object-detector step rarely changes). */
|
|
@@ -87451,10 +87904,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
87451
87904
|
overlayState = new OverlayDetailStateStore();
|
|
87452
87905
|
/** Throttle state for the "overlay synthesis failed" warn, one per device. */
|
|
87453
87906
|
overlaySynthesisWarnAt = /* @__PURE__ */ new Map();
|
|
87454
|
-
/** Active CLIP image-embedding model id (== the text encoder's), TTL-cached.
|
|
87455
|
-
* Stamped on object-embedding rows from the detail plane so semantic search's
|
|
87456
|
-
* same-model gate keeps matching. */
|
|
87457
|
-
clipModelIdCache = null;
|
|
87458
87907
|
/** Frame-based event/track media (crop + boxed full-frame) from the
|
|
87459
87908
|
* detection-pipeline DECODED frame — the ONLY image source (never the
|
|
87460
87909
|
* snapshot cap). Null when shm frame access is unavailable. */
|
|
@@ -91330,6 +91779,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
91330
91779
|
await Promise.all([this.drainSensorHandlers(), this.motionEpisodes?.closeAll("addon-shutdown")]);
|
|
91331
91780
|
await this.embeddingDispatcher?.stop();
|
|
91332
91781
|
this.embeddingDispatcher = null;
|
|
91782
|
+
this.objectEmbeddingStore?.flushRefusals();
|
|
91333
91783
|
await this.notificationCenter?.stop();
|
|
91334
91784
|
this.notificationCenter = null;
|
|
91335
91785
|
for (const id of this.proxies.keys()) this.releaseProxy(id);
|
|
@@ -93012,31 +93462,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
93012
93462
|
});
|
|
93013
93463
|
}
|
|
93014
93464
|
/** CLIP object-embedding detail → the object-embedding store (semantic
|
|
93015
|
-
* search)
|
|
93016
|
-
* gate keeps matching. */
|
|
93465
|
+
* search), under the model the RUNNER stamped — never a guess (D658). */
|
|
93017
93466
|
async routeClipDetail(deviceId, trackId, detail, timestamp) {
|
|
93018
93467
|
const store = this.objectEmbeddingStore;
|
|
93019
93468
|
if (!store || detail.embedding === void 0) return;
|
|
93020
|
-
const modelId = detail.embeddingModelId ?? await this.resolveClipModelId();
|
|
93021
|
-
if (modelId === null) {
|
|
93022
|
-
this.ctx.logger.debug("clip detail dropped — no active embedding model id", {
|
|
93023
|
-
tags: { deviceId },
|
|
93024
|
-
meta: {
|
|
93025
|
-
trackId,
|
|
93026
|
-
stepId: detail.stepId
|
|
93027
|
-
}
|
|
93028
|
-
});
|
|
93029
|
-
return;
|
|
93030
|
-
}
|
|
93031
93469
|
const keyFrameMediaId = this.residents.keyFrameMediaId(trackId);
|
|
93032
|
-
await store
|
|
93033
|
-
trackId,
|
|
93470
|
+
await routeClipDetailToStore(store, {
|
|
93034
93471
|
deviceId,
|
|
93472
|
+
trackId,
|
|
93035
93473
|
timestamp,
|
|
93036
93474
|
className: detail.className,
|
|
93037
|
-
|
|
93038
|
-
|
|
93039
|
-
|
|
93475
|
+
score: detail.score,
|
|
93476
|
+
embeddingBase64: detail.embedding,
|
|
93477
|
+
embeddingModelId: detail.embeddingModelId,
|
|
93040
93478
|
...keyFrameMediaId !== void 0 ? { keyFrameMediaId } : {}
|
|
93041
93479
|
});
|
|
93042
93480
|
}
|
|
@@ -93065,23 +93503,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
93065
93503
|
...identityId !== void 0 ? { identityId } : {}
|
|
93066
93504
|
});
|
|
93067
93505
|
}
|
|
93068
|
-
/** Active CLIP image-embedding model id (== the text encoder's), TTL-cached.
|
|
93069
|
-
* Returns null when the embedding-encoder cap is unavailable. */
|
|
93070
|
-
async resolveClipModelId() {
|
|
93071
|
-
const now = Date.now();
|
|
93072
|
-
if (this.clipModelIdCache && now < this.clipModelIdCache.expiresAt) return this.clipModelIdCache.value;
|
|
93073
|
-
let value = null;
|
|
93074
|
-
try {
|
|
93075
|
-
value = (await this.ctx.api.embeddingEncoder.getInfo.query())?.modelId ?? null;
|
|
93076
|
-
} catch (err) {
|
|
93077
|
-
this.ctx.logger.debug("resolveClipModelId: getInfo failed", { meta: { error: require_clip_model_registry.errMsg(err) } });
|
|
93078
|
-
}
|
|
93079
|
-
this.clipModelIdCache = {
|
|
93080
|
-
value,
|
|
93081
|
-
expiresAt: now + CLIP_MODEL_ID_CACHE_TTL_MS
|
|
93082
|
-
};
|
|
93083
|
-
return value;
|
|
93084
|
-
}
|
|
93085
93506
|
/**
|
|
93086
93507
|
* §5 — decide which active tracks need periodic media THIS frame. Pure over
|
|
93087
93508
|
* TrackStore.lastSnapshotAt + the per-track best-confidence map:
|
|
@@ -97007,6 +97428,126 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97007
97428
|
}
|
|
97008
97429
|
}
|
|
97009
97430
|
/**
|
|
97431
|
+
* D658: re-embed EXACTLY the named samples with ONE pinned model, forced
|
|
97432
|
+
* (`face-sample-reembed.ts`) — the repair for what the audit accuses. Shares
|
|
97433
|
+
* the gallery-job exclusion with the re-embed pass, the photo pass and the
|
|
97434
|
+
* reset, and reloads the matcher's gallery when anything landed.
|
|
97435
|
+
*/
|
|
97436
|
+
async reembedFaceGallerySamples(input) {
|
|
97437
|
+
const api = this.ctx.api;
|
|
97438
|
+
if (!api) throw new Error("faceGallery.reembedSamples: the addon API is not available");
|
|
97439
|
+
const identityStore = this.identityStore;
|
|
97440
|
+
if (!identityStore) throw new Error("faceGallery.reembedSamples: the identity store is not ready");
|
|
97441
|
+
const mediaStore = this.mediaStore;
|
|
97442
|
+
if (!mediaStore) throw new Error("faceGallery.reembedSamples: the media store is not ready");
|
|
97443
|
+
if (this.faceReembedRunning) throw new Error("faceGallery.reembedSamples: a face re-embed pass is already running");
|
|
97444
|
+
if (this.faceAlignmentJobs?.isPhotoPassRunning === true) throw new Error("faceGallery.reembedSamples: a photo re-enrol pass is running (D643)");
|
|
97445
|
+
if (this.faceAlignmentJobs?.isResetRunning === true) throw new Error("faceGallery.reembedSamples: a face gallery reset is running (D644)");
|
|
97446
|
+
this.faceReembedRunning = true;
|
|
97447
|
+
try {
|
|
97448
|
+
const embedder = new FaceReembedEmbedder({
|
|
97449
|
+
runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
|
|
97450
|
+
logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
|
|
97451
|
+
}, await this.resolveRebuildNodes(api, input.executeOnNodeId), input.modelId);
|
|
97452
|
+
const result = await reembedFaceSamples({
|
|
97453
|
+
listSamples: () => identityStore.listSamplesForReembed(),
|
|
97454
|
+
loadAlignedCrop: async (sample) => {
|
|
97455
|
+
if (sample.mediaId === void 0) return null;
|
|
97456
|
+
try {
|
|
97457
|
+
const file = await mediaStore.getById(sample.mediaId);
|
|
97458
|
+
return file === null ? null : Buffer.from(file.base64, "base64");
|
|
97459
|
+
} catch (err) {
|
|
97460
|
+
this.ctx.logger.warn("face sample re-embed: aligned crop read threw", { meta: {
|
|
97461
|
+
sampleId: sample.sampleId,
|
|
97462
|
+
error: String(err)
|
|
97463
|
+
} });
|
|
97464
|
+
return null;
|
|
97465
|
+
}
|
|
97466
|
+
},
|
|
97467
|
+
embed: (sample, crop) => embedder.embed(sample, crop),
|
|
97468
|
+
store: (sample, embedding, modelId, dim) => identityStore.restampSample({
|
|
97469
|
+
sampleId: sample.sampleId,
|
|
97470
|
+
identityId: sample.identityId,
|
|
97471
|
+
embedding,
|
|
97472
|
+
modelId,
|
|
97473
|
+
dim
|
|
97474
|
+
}),
|
|
97475
|
+
pause: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
97476
|
+
logger: {
|
|
97477
|
+
info: (m, e) => this.ctx.logger.info(m, e),
|
|
97478
|
+
warn: (m, e) => this.ctx.logger.warn(m, e)
|
|
97479
|
+
}
|
|
97480
|
+
}, {
|
|
97481
|
+
sampleIds: input.sampleIds,
|
|
97482
|
+
modelId: input.modelId
|
|
97483
|
+
});
|
|
97484
|
+
if (result.reembedded > 0) await this.faceRecognizer?.refreshGallery();
|
|
97485
|
+
return {
|
|
97486
|
+
...result,
|
|
97487
|
+
rows: [...result.rows]
|
|
97488
|
+
};
|
|
97489
|
+
} finally {
|
|
97490
|
+
this.faceReembedRunning = false;
|
|
97491
|
+
}
|
|
97492
|
+
}
|
|
97493
|
+
/**
|
|
97494
|
+
* D658: re-embed each sample's stored template with ONE pinned model and
|
|
97495
|
+
* compare with its stored vector (`face-embedding-audit.ts`). READ-ONLY —
|
|
97496
|
+
* nothing is restamped, re-embedded or removed. The pass pins the model the
|
|
97497
|
+
* caller names (default `auraface-r100`), not the cluster row: the question
|
|
97498
|
+
* is whether a STAMP is true, whatever the cluster runs today.
|
|
97499
|
+
*/
|
|
97500
|
+
async auditFaceGalleryEmbeddings(input) {
|
|
97501
|
+
const api = this.ctx.api;
|
|
97502
|
+
if (!api) throw new Error("faceGallery.auditEmbeddings: the addon API is not available");
|
|
97503
|
+
const identityStore = this.identityStore;
|
|
97504
|
+
if (!identityStore) throw new Error("faceGallery.auditEmbeddings: the identity store is not ready");
|
|
97505
|
+
const mediaStore = this.mediaStore;
|
|
97506
|
+
if (!mediaStore) throw new Error("faceGallery.auditEmbeddings: the media store is not ready");
|
|
97507
|
+
const auditedModelId = input.modelId ?? "auraface-r100";
|
|
97508
|
+
const embedder = new FaceReembedEmbedder({
|
|
97509
|
+
runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
|
|
97510
|
+
logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
|
|
97511
|
+
}, await this.resolveRebuildNodes(api, input.executeOnNodeId), auditedModelId);
|
|
97512
|
+
const report = await auditFaceEmbeddings({
|
|
97513
|
+
listSamples: () => identityStore.listSamplesForReembed(),
|
|
97514
|
+
loadStoredVectors: (ids) => identityStore.loadSampleVectors(ids),
|
|
97515
|
+
loadAlignedCrop: async (sample) => {
|
|
97516
|
+
if (sample.mediaId === void 0) return null;
|
|
97517
|
+
try {
|
|
97518
|
+
const file = await mediaStore.getById(sample.mediaId);
|
|
97519
|
+
return file === null ? null : Buffer.from(file.base64, "base64");
|
|
97520
|
+
} catch (err) {
|
|
97521
|
+
this.ctx.logger.warn("face embedding audit: aligned crop read threw", { meta: {
|
|
97522
|
+
sampleId: sample.sampleId,
|
|
97523
|
+
error: String(err)
|
|
97524
|
+
} });
|
|
97525
|
+
return null;
|
|
97526
|
+
}
|
|
97527
|
+
},
|
|
97528
|
+
embed: (sample, crop) => embedder.embed(sample, crop)
|
|
97529
|
+
}, {
|
|
97530
|
+
auditedModelId,
|
|
97531
|
+
...input.identityId !== void 0 ? { identityId: input.identityId } : {},
|
|
97532
|
+
...input.maxSamples !== void 0 ? { maxSamples: input.maxSamples } : {}
|
|
97533
|
+
});
|
|
97534
|
+
this.ctx.logger.info("face embedding audit finished", { meta: {
|
|
97535
|
+
auditedModelId,
|
|
97536
|
+
total: report.total,
|
|
97537
|
+
ok: report.ok,
|
|
97538
|
+
mismatch: report.mismatch,
|
|
97539
|
+
inconclusive: report.inconclusive,
|
|
97540
|
+
unmeasurable: report.unmeasurable,
|
|
97541
|
+
complete: report.complete
|
|
97542
|
+
} });
|
|
97543
|
+
return {
|
|
97544
|
+
...report,
|
|
97545
|
+
okMinCosine: AUDIT_OK_MIN_COSINE,
|
|
97546
|
+
mismatchMaxCosine: AUDIT_MISMATCH_MAX_COSINE,
|
|
97547
|
+
rows: [...report.rows]
|
|
97548
|
+
};
|
|
97549
|
+
}
|
|
97550
|
+
/**
|
|
97010
97551
|
* The server-side half of "change the model from any UI and the gallery
|
|
97011
97552
|
* follows": if a sample sits outside the cluster space and a pass could
|
|
97012
97553
|
* still bring it in, start one. Runs on a timer; converges and goes quiet.
|
|
@@ -97529,6 +98070,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97529
98070
|
samples: [...result.samples]
|
|
97530
98071
|
};
|
|
97531
98072
|
},
|
|
98073
|
+
"faceGallery.reembedSamples": async (input) => this.reembedFaceGallerySamples(input),
|
|
98074
|
+
"faceGallery.auditEmbeddings": async (input) => this.auditFaceGalleryEmbeddings(input),
|
|
97532
98075
|
"faceGallery.sourceFrameStats": async () => ({ devices: this.faceSourceStats.snapshot().map((d) => ({
|
|
97533
98076
|
...d,
|
|
97534
98077
|
dropped: { ...d.dropped }
|