@camstack/addon-post-analysis 1.2.286 → 1.2.288
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-CY0FGcpQ.js → clip-model-registry-eFgKIlKE.js} +63 -42
- package/dist/{clip-model-registry-D4mC2M7F.mjs → clip-model-registry-hdhXFofg.mjs} +63 -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-CBo7IWoj.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-XcjvUbbR.mjs +26 -0
- 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-Fpmz4x_M.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +636 -74
- package/dist/pipeline-analytics/index.mjs +636 -74
- 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_types__loadShare__.js-NKbCrEkH.mjs +0 -26
- 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-eFgKIlKE.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) {
|
|
@@ -235,6 +235,34 @@ var MotionEpisodeTracker = class {
|
|
|
235
235
|
}
|
|
236
236
|
};
|
|
237
237
|
//#endregion
|
|
238
|
+
//#region src/pipeline-analytics/audio-classify-outcome.ts
|
|
239
|
+
/**
|
|
240
|
+
* The Notification Center's audio sample for one analyzer frame: its level,
|
|
241
|
+
* the labels that clear the SAME per-device floor the router uses (so a rule
|
|
242
|
+
* and the event log never disagree about what was heard), and whether the
|
|
243
|
+
* model ran at all.
|
|
244
|
+
*/
|
|
245
|
+
function ncAudioSampleFromFrame(frame, timestampMs, floor) {
|
|
246
|
+
const dbfs = frame.level?.dbfs;
|
|
247
|
+
return {
|
|
248
|
+
timestampMs,
|
|
249
|
+
...dbfs !== void 0 ? { dbfs } : {},
|
|
250
|
+
labels: (frame.detections ?? []).filter((d) => d.score >= floor.classificationMinScore).map((d) => d.macroClass).filter((macroClass) => floor.audioAllowedClasses.length === 0 || floor.audioAllowedClasses.includes(macroClass)),
|
|
251
|
+
...frame.classifyOutcome !== void 0 ? { classifyOutcome: frame.classifyOutcome } : {}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/** The level-path audio row for one frame — the one row kind with no label to explain it. */
|
|
255
|
+
function levelAudioEventDraft(deviceId, timestamp, frame) {
|
|
256
|
+
return {
|
|
257
|
+
deviceId,
|
|
258
|
+
timestamp,
|
|
259
|
+
kind: "audio",
|
|
260
|
+
rms: frame.level?.rms ?? 0,
|
|
261
|
+
dbfs: frame.level?.dbfs ?? 0,
|
|
262
|
+
...frame.classifyOutcome !== void 0 ? { classifyOutcome: frame.classifyOutcome } : {}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
238
266
|
//#region src/notification-center/alarm/alarm-mode-coverage.ts
|
|
239
267
|
/**
|
|
240
268
|
* Whether the panel just BECAME armed, in the sense a person means.
|
|
@@ -12780,14 +12808,14 @@ var NcPruneRecordSchema = require_clip_model_registry.object({
|
|
|
12780
12808
|
});
|
|
12781
12809
|
/** Rules × deleted subjects — small; a generous ceiling. */
|
|
12782
12810
|
var LOAD_LIMIT$4 = 1e4;
|
|
12783
|
-
function keyOf(row) {
|
|
12811
|
+
function keyOf$1(row) {
|
|
12784
12812
|
return `${row.kind}:${row.ruleId}:${row.field}:${row.subject}`;
|
|
12785
12813
|
}
|
|
12786
12814
|
var NC_DEVICE_PRUNES_SPEC = {
|
|
12787
12815
|
collection: NC_DEVICE_PRUNES_COLLECTION,
|
|
12788
12816
|
columns: [...NC_DEVICE_PRUNES_COLUMNS],
|
|
12789
12817
|
writeMode: "write-through",
|
|
12790
|
-
keyOf,
|
|
12818
|
+
keyOf: keyOf$1,
|
|
12791
12819
|
toValue: (row) => ({
|
|
12792
12820
|
ruleId: row.ruleId,
|
|
12793
12821
|
at: row.at,
|
|
@@ -12827,7 +12855,7 @@ var NcPruneLedger = class {
|
|
|
12827
12855
|
* {@link isLoaded} as it was — see the module docblock.
|
|
12828
12856
|
*/
|
|
12829
12857
|
async load() {
|
|
12830
|
-
const heldBefore = new Set(this.ledger.snapshot().map(keyOf));
|
|
12858
|
+
const heldBefore = new Set(this.ledger.snapshot().map(keyOf$1));
|
|
12831
12859
|
const startedAt = this.writes;
|
|
12832
12860
|
const writtenSince = (key) => (this.writtenAt.get(key) ?? 0) > startedAt;
|
|
12833
12861
|
try {
|
|
@@ -12838,7 +12866,7 @@ var NcPruneLedger = class {
|
|
|
12838
12866
|
const next = /* @__PURE__ */ new Map();
|
|
12839
12867
|
for (const record of records) {
|
|
12840
12868
|
const row = NC_DEVICE_PRUNES_SPEC.fromRecord(record.id, record.data);
|
|
12841
|
-
if (row !== null) next.set(keyOf(row), row);
|
|
12869
|
+
if (row !== null) next.set(keyOf$1(row), row);
|
|
12842
12870
|
}
|
|
12843
12871
|
for (const key of heldBefore) if (!next.has(key) && !writtenSince(key)) this.ledger.evict(key);
|
|
12844
12872
|
for (const [key, row] of next) if (!writtenSince(key)) this.ledger.stage(row);
|
|
@@ -12868,7 +12896,7 @@ var NcPruneLedger = class {
|
|
|
12868
12896
|
*/
|
|
12869
12897
|
async record(notes, source, at) {
|
|
12870
12898
|
for (const note of notes) {
|
|
12871
|
-
const held = this.ledger.get(keyOf(note));
|
|
12899
|
+
const held = this.ledger.get(keyOf$1(note));
|
|
12872
12900
|
if (held !== void 0 && KEPT_OUTCOMES.has(held.outcome) && !KEPT_OUTCOMES.has(note.outcome)) continue;
|
|
12873
12901
|
const row = NcPruneRecordSchema.parse({
|
|
12874
12902
|
...note,
|
|
@@ -12876,7 +12904,7 @@ var NcPruneLedger = class {
|
|
|
12876
12904
|
source
|
|
12877
12905
|
});
|
|
12878
12906
|
this.writes += 1;
|
|
12879
|
-
this.writtenAt.set(keyOf(row), this.writes);
|
|
12907
|
+
this.writtenAt.set(keyOf$1(row), this.writes);
|
|
12880
12908
|
await this.ledger.put(row);
|
|
12881
12909
|
}
|
|
12882
12910
|
}
|
|
@@ -39708,6 +39736,87 @@ var ReembedFacesInputSchema = require_clip_model_registry.object({
|
|
|
39708
39736
|
/** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
|
|
39709
39737
|
executeOnNodeId: require_clip_model_registry.string().optional()
|
|
39710
39738
|
});
|
|
39739
|
+
/**
|
|
39740
|
+
* D658: re-embed each sample's stored template with ONE pinned model and
|
|
39741
|
+
* compare with the stored vector — does the stamp say what produced it?
|
|
39742
|
+
* Read-only: it reports and writes nothing.
|
|
39743
|
+
*/
|
|
39744
|
+
var AuditFaceEmbeddingsInputSchema = require_clip_model_registry.object({
|
|
39745
|
+
/** The stamp to verify. Omit for `auraface-r100` (the D651 default). */
|
|
39746
|
+
modelId: require_clip_model_registry.string().min(1).optional(),
|
|
39747
|
+
/** Only this identity's samples. */
|
|
39748
|
+
identityId: require_clip_model_registry.string().min(1).optional(),
|
|
39749
|
+
/** Stop after this many measured samples. */
|
|
39750
|
+
maxSamples: require_clip_model_registry.number().int().positive().optional(),
|
|
39751
|
+
/** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
|
|
39752
|
+
executeOnNodeId: require_clip_model_registry.string().optional()
|
|
39753
|
+
});
|
|
39754
|
+
var AuditFaceEmbeddingsResultSchema = require_clip_model_registry.object({
|
|
39755
|
+
auditedModelId: require_clip_model_registry.string(),
|
|
39756
|
+
total: require_clip_model_registry.number(),
|
|
39757
|
+
ok: require_clip_model_registry.number(),
|
|
39758
|
+
mismatch: require_clip_model_registry.number(),
|
|
39759
|
+
inconclusive: require_clip_model_registry.number(),
|
|
39760
|
+
unmeasurable: require_clip_model_registry.number(),
|
|
39761
|
+
complete: require_clip_model_registry.boolean(),
|
|
39762
|
+
okMinCosine: require_clip_model_registry.number(),
|
|
39763
|
+
mismatchMaxCosine: require_clip_model_registry.number(),
|
|
39764
|
+
rows: require_clip_model_registry.array(require_clip_model_registry.object({
|
|
39765
|
+
sampleId: require_clip_model_registry.string(),
|
|
39766
|
+
identityId: require_clip_model_registry.string(),
|
|
39767
|
+
identityName: require_clip_model_registry.string(),
|
|
39768
|
+
stampedModelId: require_clip_model_registry.string(),
|
|
39769
|
+
verdict: require_clip_model_registry._enum([
|
|
39770
|
+
"ok",
|
|
39771
|
+
"mismatch",
|
|
39772
|
+
"inconclusive",
|
|
39773
|
+
"unmeasurable"
|
|
39774
|
+
]),
|
|
39775
|
+
cosine: require_clip_model_registry.number().optional(),
|
|
39776
|
+
reason: require_clip_model_registry._enum([
|
|
39777
|
+
"stamped-other-model",
|
|
39778
|
+
"no-pixels",
|
|
39779
|
+
"no-stored-vector",
|
|
39780
|
+
"embed-failed",
|
|
39781
|
+
"no-capable-node",
|
|
39782
|
+
"dimension-differs"
|
|
39783
|
+
]).optional(),
|
|
39784
|
+
detail: require_clip_model_registry.string().optional()
|
|
39785
|
+
}))
|
|
39786
|
+
});
|
|
39787
|
+
/**
|
|
39788
|
+
* D658: re-embed EXACTLY these samples with ONE pinned model, replacing their
|
|
39789
|
+
* vectors even when already stamped with it — the repair for what
|
|
39790
|
+
* `faceGallery.auditEmbeddings` accuses. See `face-sample-reembed.ts`.
|
|
39791
|
+
*/
|
|
39792
|
+
var ReembedSamplesInputSchema = require_clip_model_registry.object({
|
|
39793
|
+
sampleIds: require_clip_model_registry.array(require_clip_model_registry.string().min(1)).min(1).max(200),
|
|
39794
|
+
modelId: require_clip_model_registry.string().min(1),
|
|
39795
|
+
/** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
|
|
39796
|
+
executeOnNodeId: require_clip_model_registry.string().optional()
|
|
39797
|
+
});
|
|
39798
|
+
var ReembedSamplesResultSchema = require_clip_model_registry.object({
|
|
39799
|
+
pinnedModelId: require_clip_model_registry.string(),
|
|
39800
|
+
requested: require_clip_model_registry.number(),
|
|
39801
|
+
reembedded: require_clip_model_registry.number(),
|
|
39802
|
+
failed: require_clip_model_registry.number(),
|
|
39803
|
+
rows: require_clip_model_registry.array(require_clip_model_registry.object({
|
|
39804
|
+
sampleId: require_clip_model_registry.string(),
|
|
39805
|
+
outcome: require_clip_model_registry._enum([
|
|
39806
|
+
"reembedded",
|
|
39807
|
+
"not-found",
|
|
39808
|
+
"duplicate",
|
|
39809
|
+
"no-pixels",
|
|
39810
|
+
"embed-failed",
|
|
39811
|
+
"no-capable-node",
|
|
39812
|
+
"no-model-stamp",
|
|
39813
|
+
"not-landed"
|
|
39814
|
+
]),
|
|
39815
|
+
previousModelId: require_clip_model_registry.string().optional(),
|
|
39816
|
+
modelId: require_clip_model_registry.string().optional(),
|
|
39817
|
+
detail: require_clip_model_registry.string().optional()
|
|
39818
|
+
}))
|
|
39819
|
+
});
|
|
39711
39820
|
/** Mirrors `FaceReembedResult` (`face-reembed-pass.ts`) minus the sample-id set. */
|
|
39712
39821
|
var ReembedFacesResultSchema = require_clip_model_registry.object({
|
|
39713
39822
|
targetModelId: require_clip_model_registry.string(),
|
|
@@ -39959,11 +40068,256 @@ var embeddingActions = require_clip_model_registry.defineCustomActions({
|
|
|
39959
40068
|
"embedding.faceAlignmentStatus": require_clip_model_registry.customAction(NoInputSchema$1, FaceAlignmentStatusSchema),
|
|
39960
40069
|
"embedding.backfillPhotoSources": require_clip_model_registry.customAction(BackfillPhotoSourcesInputSchema, BackfillPhotoSourcesResultSchema, { auth: "admin" }),
|
|
39961
40070
|
"faceGallery.sourceFrameStats": require_clip_model_registry.customAction(NoInputSchema$1, SourceFrameStatsSchema),
|
|
40071
|
+
"faceGallery.reembedSamples": require_clip_model_registry.customAction(ReembedSamplesInputSchema, ReembedSamplesResultSchema, {
|
|
40072
|
+
kind: "mutation",
|
|
40073
|
+
auth: "admin"
|
|
40074
|
+
}),
|
|
40075
|
+
"faceGallery.auditEmbeddings": require_clip_model_registry.customAction(AuditFaceEmbeddingsInputSchema, AuditFaceEmbeddingsResultSchema, { auth: "admin" }),
|
|
39962
40076
|
"faceGallery.resetToCore": require_clip_model_registry.customAction(ResetToCoreInputSchema, ResetToCoreResultSchema, {
|
|
39963
40077
|
kind: "mutation",
|
|
39964
40078
|
auth: "admin"
|
|
39965
40079
|
})
|
|
39966
40080
|
});
|
|
40081
|
+
async function reembedOne(deps, sample, pinned) {
|
|
40082
|
+
const base = {
|
|
40083
|
+
sampleId: sample.sampleId,
|
|
40084
|
+
previousModelId: sample.modelId
|
|
40085
|
+
};
|
|
40086
|
+
const crop = await deps.loadAlignedCrop(sample);
|
|
40087
|
+
if (crop === null) return {
|
|
40088
|
+
...base,
|
|
40089
|
+
outcome: "no-pixels"
|
|
40090
|
+
};
|
|
40091
|
+
const outcome = await deps.embed(sample, crop);
|
|
40092
|
+
if (outcome.kind === "failed") return {
|
|
40093
|
+
...base,
|
|
40094
|
+
outcome: "embed-failed",
|
|
40095
|
+
detail: outcome.reason
|
|
40096
|
+
};
|
|
40097
|
+
if (outcome.kind === "no-capable-node") return {
|
|
40098
|
+
...base,
|
|
40099
|
+
outcome: "no-capable-node",
|
|
40100
|
+
detail: outcome.reason
|
|
40101
|
+
};
|
|
40102
|
+
if (outcome.modelId === void 0 || outcome.modelId === "") return {
|
|
40103
|
+
...base,
|
|
40104
|
+
outcome: "no-model-stamp",
|
|
40105
|
+
detail: "the embed answered no model id"
|
|
40106
|
+
};
|
|
40107
|
+
if (outcome.modelId !== pinned) return {
|
|
40108
|
+
...base,
|
|
40109
|
+
outcome: "embed-failed",
|
|
40110
|
+
detail: `ran "${outcome.modelId}" for a call pinned to "${pinned}"`
|
|
40111
|
+
};
|
|
40112
|
+
const write = await deps.store(sample, outcome.embedding, outcome.modelId, outcome.dim);
|
|
40113
|
+
if (!write.landed) return {
|
|
40114
|
+
...base,
|
|
40115
|
+
outcome: "not-landed",
|
|
40116
|
+
detail: write.reason ?? "the write did not land"
|
|
40117
|
+
};
|
|
40118
|
+
return {
|
|
40119
|
+
...base,
|
|
40120
|
+
outcome: "reembedded",
|
|
40121
|
+
modelId: outcome.modelId
|
|
40122
|
+
};
|
|
40123
|
+
}
|
|
40124
|
+
async function reembedFaceSamples(deps, options) {
|
|
40125
|
+
const pinned = options.modelId;
|
|
40126
|
+
const byId = new Map((await deps.listSamples()).map((s) => [s.sampleId, s]));
|
|
40127
|
+
const pacingMs = options.pacingMs ?? 25;
|
|
40128
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40129
|
+
const rows = [];
|
|
40130
|
+
let noNode = null;
|
|
40131
|
+
deps.logger.info("face sample re-embed starting", { meta: {
|
|
40132
|
+
pinnedModelId: pinned,
|
|
40133
|
+
requested: options.sampleIds.length
|
|
40134
|
+
} });
|
|
40135
|
+
for (const sampleId of options.sampleIds) {
|
|
40136
|
+
if (seen.has(sampleId)) {
|
|
40137
|
+
rows.push({
|
|
40138
|
+
sampleId,
|
|
40139
|
+
outcome: "duplicate"
|
|
40140
|
+
});
|
|
40141
|
+
continue;
|
|
40142
|
+
}
|
|
40143
|
+
seen.add(sampleId);
|
|
40144
|
+
const sample = byId.get(sampleId);
|
|
40145
|
+
if (sample === void 0) {
|
|
40146
|
+
rows.push({
|
|
40147
|
+
sampleId,
|
|
40148
|
+
outcome: "not-found"
|
|
40149
|
+
});
|
|
40150
|
+
continue;
|
|
40151
|
+
}
|
|
40152
|
+
if (noNode !== null) {
|
|
40153
|
+
rows.push({
|
|
40154
|
+
sampleId,
|
|
40155
|
+
previousModelId: sample.modelId,
|
|
40156
|
+
outcome: "no-capable-node",
|
|
40157
|
+
detail: noNode
|
|
40158
|
+
});
|
|
40159
|
+
continue;
|
|
40160
|
+
}
|
|
40161
|
+
const row = await reembedOne(deps, sample, pinned);
|
|
40162
|
+
if (row.outcome === "no-capable-node") noNode = row.detail ?? "no capable node";
|
|
40163
|
+
if (row.outcome !== "reembedded") deps.logger.warn("face sample re-embed: sample NOT re-embedded", { meta: {
|
|
40164
|
+
sampleId,
|
|
40165
|
+
outcome: row.outcome,
|
|
40166
|
+
detail: row.detail ?? null,
|
|
40167
|
+
pinnedModelId: pinned
|
|
40168
|
+
} });
|
|
40169
|
+
rows.push(row);
|
|
40170
|
+
await deps.pause(pacingMs);
|
|
40171
|
+
}
|
|
40172
|
+
const reembedded = rows.filter((r) => r.outcome === "reembedded").length;
|
|
40173
|
+
deps.logger.info("face sample re-embed finished", { meta: {
|
|
40174
|
+
pinnedModelId: pinned,
|
|
40175
|
+
requested: options.sampleIds.length,
|
|
40176
|
+
reembedded,
|
|
40177
|
+
failed: rows.length - reembedded
|
|
40178
|
+
} });
|
|
40179
|
+
return {
|
|
40180
|
+
pinnedModelId: pinned,
|
|
40181
|
+
requested: options.sampleIds.length,
|
|
40182
|
+
reembedded,
|
|
40183
|
+
failed: rows.length - reembedded,
|
|
40184
|
+
rows
|
|
40185
|
+
};
|
|
40186
|
+
}
|
|
40187
|
+
//#endregion
|
|
40188
|
+
//#region src/pipeline-analytics/face-embedding-audit.ts
|
|
40189
|
+
/** At or above: the stored vector is this model's. */
|
|
40190
|
+
var AUDIT_OK_MIN_COSINE = .98;
|
|
40191
|
+
/** Below: the stored vector is NOT this model's. Between the two: inconclusive. */
|
|
40192
|
+
var AUDIT_MISMATCH_MAX_COSINE = .9;
|
|
40193
|
+
/** Cosine of two vectors of equal length; `null` when either has no length. */
|
|
40194
|
+
function cosine(a, b) {
|
|
40195
|
+
let dot = 0;
|
|
40196
|
+
let na = 0;
|
|
40197
|
+
let nb = 0;
|
|
40198
|
+
for (let i = 0; i < a.length; i++) {
|
|
40199
|
+
const x = a[i] ?? 0;
|
|
40200
|
+
const y = b[i] ?? 0;
|
|
40201
|
+
dot += x * y;
|
|
40202
|
+
na += x * x;
|
|
40203
|
+
nb += y * y;
|
|
40204
|
+
}
|
|
40205
|
+
if (na === 0 || nb === 0) return null;
|
|
40206
|
+
return dot / Math.sqrt(na * nb);
|
|
40207
|
+
}
|
|
40208
|
+
/** The verdict a measured cosine earns. Pure. */
|
|
40209
|
+
function verdictOf(c) {
|
|
40210
|
+
if (c >= .98) return "ok";
|
|
40211
|
+
if (c < .9) return "mismatch";
|
|
40212
|
+
return "inconclusive";
|
|
40213
|
+
}
|
|
40214
|
+
function rowBase(s) {
|
|
40215
|
+
return {
|
|
40216
|
+
sampleId: s.sampleId,
|
|
40217
|
+
identityId: s.identityId,
|
|
40218
|
+
identityName: s.identityName,
|
|
40219
|
+
stampedModelId: s.modelId
|
|
40220
|
+
};
|
|
40221
|
+
}
|
|
40222
|
+
async function measure$2(deps, sample, stored) {
|
|
40223
|
+
const base = rowBase(sample);
|
|
40224
|
+
if (stored === void 0) return {
|
|
40225
|
+
...base,
|
|
40226
|
+
verdict: "unmeasurable",
|
|
40227
|
+
reason: "no-stored-vector"
|
|
40228
|
+
};
|
|
40229
|
+
const crop = await deps.loadAlignedCrop(sample);
|
|
40230
|
+
if (crop === null) return {
|
|
40231
|
+
...base,
|
|
40232
|
+
verdict: "unmeasurable",
|
|
40233
|
+
reason: "no-pixels"
|
|
40234
|
+
};
|
|
40235
|
+
const outcome = await deps.embed(sample, crop);
|
|
40236
|
+
if (outcome.kind === "failed") return {
|
|
40237
|
+
...base,
|
|
40238
|
+
verdict: "unmeasurable",
|
|
40239
|
+
reason: "embed-failed",
|
|
40240
|
+
detail: outcome.reason
|
|
40241
|
+
};
|
|
40242
|
+
if (outcome.kind === "no-capable-node") return {
|
|
40243
|
+
...base,
|
|
40244
|
+
verdict: "unmeasurable",
|
|
40245
|
+
reason: "no-capable-node",
|
|
40246
|
+
detail: outcome.reason
|
|
40247
|
+
};
|
|
40248
|
+
if (outcome.embedding.length !== stored.length) return {
|
|
40249
|
+
...base,
|
|
40250
|
+
verdict: "unmeasurable",
|
|
40251
|
+
reason: "dimension-differs",
|
|
40252
|
+
detail: `stored ${stored.length}-d, re-embedded ${outcome.embedding.length}-d`
|
|
40253
|
+
};
|
|
40254
|
+
const c = cosine(outcome.embedding, stored);
|
|
40255
|
+
if (c === null) return {
|
|
40256
|
+
...base,
|
|
40257
|
+
verdict: "unmeasurable",
|
|
40258
|
+
reason: "no-stored-vector",
|
|
40259
|
+
detail: "zero vector"
|
|
40260
|
+
};
|
|
40261
|
+
return {
|
|
40262
|
+
...base,
|
|
40263
|
+
verdict: verdictOf(c),
|
|
40264
|
+
cosine: Math.round(c * 1e4) / 1e4
|
|
40265
|
+
};
|
|
40266
|
+
}
|
|
40267
|
+
/**
|
|
40268
|
+
* Audit every sample stamped with `auditedModelId`. Read-only; bounded by
|
|
40269
|
+
* `maxSamples`; a pass-terminal `no-capable-node` stops measuring and reports
|
|
40270
|
+
* the rest as unmeasurable rather than asking again per sample.
|
|
40271
|
+
*/
|
|
40272
|
+
async function auditFaceEmbeddings(deps, options) {
|
|
40273
|
+
const all = await deps.listSamples();
|
|
40274
|
+
const samples = options.identityId === void 0 ? all : all.filter((s) => s.identityId === options.identityId);
|
|
40275
|
+
const inSpace = samples.filter((s) => s.modelId === options.auditedModelId);
|
|
40276
|
+
const stored = await deps.loadStoredVectors(inSpace.map((s) => s.sampleId));
|
|
40277
|
+
const budget = options.maxSamples ?? Number.POSITIVE_INFINITY;
|
|
40278
|
+
const rows = [];
|
|
40279
|
+
let measured = 0;
|
|
40280
|
+
let complete = true;
|
|
40281
|
+
let noNode = null;
|
|
40282
|
+
for (const sample of samples) {
|
|
40283
|
+
if (sample.modelId !== options.auditedModelId) {
|
|
40284
|
+
rows.push({
|
|
40285
|
+
...rowBase(sample),
|
|
40286
|
+
verdict: "unmeasurable",
|
|
40287
|
+
reason: "stamped-other-model"
|
|
40288
|
+
});
|
|
40289
|
+
continue;
|
|
40290
|
+
}
|
|
40291
|
+
if (measured >= budget) {
|
|
40292
|
+
complete = false;
|
|
40293
|
+
continue;
|
|
40294
|
+
}
|
|
40295
|
+
if (noNode !== null) {
|
|
40296
|
+
rows.push({
|
|
40297
|
+
...rowBase(sample),
|
|
40298
|
+
verdict: "unmeasurable",
|
|
40299
|
+
reason: "no-capable-node",
|
|
40300
|
+
detail: noNode
|
|
40301
|
+
});
|
|
40302
|
+
continue;
|
|
40303
|
+
}
|
|
40304
|
+
const row = await measure$2(deps, sample, stored.get(sample.sampleId));
|
|
40305
|
+
if (row.reason === "no-capable-node") noNode = row.detail ?? "no capable node";
|
|
40306
|
+
if (row.cosine !== void 0) measured += 1;
|
|
40307
|
+
rows.push(row);
|
|
40308
|
+
}
|
|
40309
|
+
const count = (v) => rows.filter((r) => r.verdict === v).length;
|
|
40310
|
+
return {
|
|
40311
|
+
auditedModelId: options.auditedModelId,
|
|
40312
|
+
total: samples.length,
|
|
40313
|
+
ok: count("ok"),
|
|
40314
|
+
mismatch: count("mismatch"),
|
|
40315
|
+
inconclusive: count("inconclusive"),
|
|
40316
|
+
unmeasurable: count("unmeasurable"),
|
|
40317
|
+
complete,
|
|
40318
|
+
rows
|
|
40319
|
+
};
|
|
40320
|
+
}
|
|
39967
40321
|
//#endregion
|
|
39968
40322
|
//#region src/pipeline-analytics/face-alignment-version.ts
|
|
39969
40323
|
/**
|
|
@@ -66007,7 +66361,8 @@ var FaceReembedEmbedder = class {
|
|
|
66007
66361
|
return {
|
|
66008
66362
|
kind: "embedded",
|
|
66009
66363
|
embedding: vector,
|
|
66010
|
-
dim: vector.length
|
|
66364
|
+
dim: vector.length,
|
|
66365
|
+
modelId: result.modelId
|
|
66011
66366
|
};
|
|
66012
66367
|
}
|
|
66013
66368
|
}
|
|
@@ -84298,6 +84653,79 @@ async function resolveSubjectShot(ports, trackId, deviceId, aroundMs) {
|
|
|
84298
84653
|
frameHeight: dims.h
|
|
84299
84654
|
};
|
|
84300
84655
|
}
|
|
84656
|
+
var MESSAGES = {
|
|
84657
|
+
"dimension-mismatch": "object embeddings refused — their length is not their model's dimension",
|
|
84658
|
+
"no-clip-metadata": "object embeddings refused — the model has no CLIP metadata",
|
|
84659
|
+
"no-model-id": "object embeddings refused — the result names no model"
|
|
84660
|
+
};
|
|
84661
|
+
function keyOf(r) {
|
|
84662
|
+
return `${String(r.deviceId)}|${r.modelId ?? ""}|${r.reason}`;
|
|
84663
|
+
}
|
|
84664
|
+
var EmbeddingRefusalReport = class {
|
|
84665
|
+
logger;
|
|
84666
|
+
intervalMs;
|
|
84667
|
+
now;
|
|
84668
|
+
keys = /* @__PURE__ */ new Map();
|
|
84669
|
+
constructor(options) {
|
|
84670
|
+
this.logger = options.logger;
|
|
84671
|
+
this.intervalMs = options.intervalMs ?? 3e5;
|
|
84672
|
+
this.now = options.now ?? (() => Date.now());
|
|
84673
|
+
}
|
|
84674
|
+
/** Count one refusal; write a line if this key is new or its interval passed. */
|
|
84675
|
+
note(refusal) {
|
|
84676
|
+
const key = keyOf(refusal);
|
|
84677
|
+
const at = this.now();
|
|
84678
|
+
const held = this.keys.get(key);
|
|
84679
|
+
const pending = (held?.pending ?? 0) + 1;
|
|
84680
|
+
if (held === void 0 || at - held.lastLineAtMs >= this.intervalMs) {
|
|
84681
|
+
this.write(refusal, pending, held === void 0 ? "first" : "interval");
|
|
84682
|
+
this.keys = new Map(this.keys).set(key, {
|
|
84683
|
+
last: refusal,
|
|
84684
|
+
pending: 0,
|
|
84685
|
+
lastLineAtMs: at
|
|
84686
|
+
});
|
|
84687
|
+
return;
|
|
84688
|
+
}
|
|
84689
|
+
this.keys = new Map(this.keys).set(key, {
|
|
84690
|
+
...held,
|
|
84691
|
+
last: refusal,
|
|
84692
|
+
pending
|
|
84693
|
+
});
|
|
84694
|
+
}
|
|
84695
|
+
/** Report every key whose interval passed with refusals still pending. */
|
|
84696
|
+
sweep() {
|
|
84697
|
+
const at = this.now();
|
|
84698
|
+
for (const [key, held] of this.keys) {
|
|
84699
|
+
if (held.pending === 0 || at - held.lastLineAtMs < this.intervalMs) continue;
|
|
84700
|
+
this.write(held.last, held.pending, "interval");
|
|
84701
|
+
this.keys = new Map(this.keys).set(key, {
|
|
84702
|
+
...held,
|
|
84703
|
+
pending: 0,
|
|
84704
|
+
lastLineAtMs: at
|
|
84705
|
+
});
|
|
84706
|
+
}
|
|
84707
|
+
}
|
|
84708
|
+
/** Report every pending count now, whatever its interval (shutdown). */
|
|
84709
|
+
flush() {
|
|
84710
|
+
for (const held of this.keys.values()) if (held.pending > 0) this.write(held.last, held.pending, "flush");
|
|
84711
|
+
this.keys = /* @__PURE__ */ new Map();
|
|
84712
|
+
}
|
|
84713
|
+
write(r, refused, closing) {
|
|
84714
|
+
this.logger.warn(MESSAGES[r.reason], {
|
|
84715
|
+
tags: { deviceId: r.deviceId },
|
|
84716
|
+
meta: {
|
|
84717
|
+
reason: r.reason,
|
|
84718
|
+
modelId: r.modelId,
|
|
84719
|
+
refused,
|
|
84720
|
+
windowMs: this.intervalMs,
|
|
84721
|
+
closing,
|
|
84722
|
+
lastTrackId: r.trackId,
|
|
84723
|
+
...r.dim !== void 0 ? { dim: r.dim } : {},
|
|
84724
|
+
...r.expected !== void 0 ? { expected: r.expected } : {}
|
|
84725
|
+
}
|
|
84726
|
+
});
|
|
84727
|
+
}
|
|
84728
|
+
};
|
|
84301
84729
|
//#endregion
|
|
84302
84730
|
//#region src/pipeline-analytics/store/object-embedding-store.ts
|
|
84303
84731
|
/**
|
|
@@ -84360,10 +84788,33 @@ var ObjectEmbeddingStore = class {
|
|
|
84360
84788
|
clipModels;
|
|
84361
84789
|
/** index → the dim this process declared it at. Per index, per process. */
|
|
84362
84790
|
declaredDims = /* @__PURE__ */ new Map();
|
|
84791
|
+
/**
|
|
84792
|
+
* Refused writes, COUNTED per (camera, model, reason). A refusal is systemic
|
|
84793
|
+
* — a runner stamping the wrong model refuses every vector it sends — so a
|
|
84794
|
+
* per-track WARN was a flood that hid the one fact that mattered (D658).
|
|
84795
|
+
*/
|
|
84796
|
+
refusals;
|
|
84363
84797
|
constructor(deps) {
|
|
84364
84798
|
this.vectorStore = deps.vectorStore;
|
|
84365
84799
|
this.logger = deps.logger;
|
|
84366
84800
|
this.clipModels = deps.clipModels;
|
|
84801
|
+
this.refusals = new EmbeddingRefusalReport({
|
|
84802
|
+
logger: deps.logger,
|
|
84803
|
+
...deps.refusalReportIntervalMs !== void 0 ? { intervalMs: deps.refusalReportIntervalMs } : {},
|
|
84804
|
+
...deps.now !== void 0 ? { now: deps.now } : {}
|
|
84805
|
+
});
|
|
84806
|
+
}
|
|
84807
|
+
/**
|
|
84808
|
+
* Count a write refused BEFORE it reached this store — a detail whose result
|
|
84809
|
+
* named no model (D658). Same report, same rate limit, as the refusals the
|
|
84810
|
+
* store makes itself.
|
|
84811
|
+
*/
|
|
84812
|
+
noteRefused(refusal) {
|
|
84813
|
+
this.refusals.note(refusal);
|
|
84814
|
+
}
|
|
84815
|
+
/** Report every refusal count still pending (shutdown). */
|
|
84816
|
+
flushRefusals() {
|
|
84817
|
+
this.refusals.flush();
|
|
84367
84818
|
}
|
|
84368
84819
|
/** Every index a delete, scan or wipe must reach. */
|
|
84369
84820
|
indexes() {
|
|
@@ -84461,19 +84912,28 @@ var ObjectEmbeddingStore = class {
|
|
|
84461
84912
|
* and dropped", because a counter that cannot reads as verification.
|
|
84462
84913
|
*/
|
|
84463
84914
|
async upsertIfBetter(input) {
|
|
84915
|
+
this.refusals.sweep();
|
|
84464
84916
|
const vector = resolveVectorPayload(input);
|
|
84465
84917
|
if (vector === null) return "failed";
|
|
84466
|
-
const space = this.
|
|
84467
|
-
if (space === null)
|
|
84918
|
+
const space = this.clipModels.spaceFor(input.modelId);
|
|
84919
|
+
if (space === null) {
|
|
84920
|
+
this.refusals.note({
|
|
84921
|
+
deviceId: input.deviceId,
|
|
84922
|
+
modelId: input.modelId,
|
|
84923
|
+
reason: "no-clip-metadata",
|
|
84924
|
+
trackId: input.trackId,
|
|
84925
|
+
dim: vector.dim
|
|
84926
|
+
});
|
|
84927
|
+
return "failed";
|
|
84928
|
+
}
|
|
84468
84929
|
if (vector.dim !== space.dim) {
|
|
84469
|
-
this.
|
|
84470
|
-
|
|
84471
|
-
|
|
84472
|
-
|
|
84473
|
-
|
|
84474
|
-
|
|
84475
|
-
|
|
84476
|
-
}
|
|
84930
|
+
this.refusals.note({
|
|
84931
|
+
deviceId: input.deviceId,
|
|
84932
|
+
modelId: input.modelId,
|
|
84933
|
+
reason: "dimension-mismatch",
|
|
84934
|
+
trackId: input.trackId,
|
|
84935
|
+
dim: vector.dim,
|
|
84936
|
+
expected: space.dim
|
|
84477
84937
|
});
|
|
84478
84938
|
return "failed";
|
|
84479
84939
|
}
|
|
@@ -84947,6 +85407,30 @@ function rowFromMetadata(id, metadata) {
|
|
|
84947
85407
|
};
|
|
84948
85408
|
}
|
|
84949
85409
|
//#endregion
|
|
85410
|
+
//#region src/pipeline-analytics/clip-detail-route.ts
|
|
85411
|
+
async function routeClipDetailToStore(store, input) {
|
|
85412
|
+
const modelId = input.embeddingModelId;
|
|
85413
|
+
if (modelId === void 0 || modelId === "") {
|
|
85414
|
+
store.noteRefused({
|
|
85415
|
+
deviceId: input.deviceId,
|
|
85416
|
+
modelId: null,
|
|
85417
|
+
reason: "no-model-id",
|
|
85418
|
+
trackId: input.trackId
|
|
85419
|
+
});
|
|
85420
|
+
return "failed";
|
|
85421
|
+
}
|
|
85422
|
+
return store.upsertIfBetter({
|
|
85423
|
+
trackId: input.trackId,
|
|
85424
|
+
deviceId: input.deviceId,
|
|
85425
|
+
timestamp: input.timestamp,
|
|
85426
|
+
className: input.className,
|
|
85427
|
+
embeddingBase64: input.embeddingBase64,
|
|
85428
|
+
modelId,
|
|
85429
|
+
confidence: input.score,
|
|
85430
|
+
...input.keyFrameMediaId !== void 0 ? { keyFrameMediaId: input.keyFrameMediaId } : {}
|
|
85431
|
+
});
|
|
85432
|
+
}
|
|
85433
|
+
//#endregion
|
|
84950
85434
|
//#region src/pipeline-analytics/clip-space-plan.ts
|
|
84951
85435
|
/**
|
|
84952
85436
|
* Which CLIP feature space a search reads and a rebuild fills — decided in one
|
|
@@ -87158,9 +87642,6 @@ var KEYFRAME_CROP_MIN_SUBJECT_IOU = .5;
|
|
|
87158
87642
|
* covers a healthy trail many times over; past it the honest answer is "no
|
|
87159
87643
|
* evidence" and the temporal bound takes over. */
|
|
87160
87644
|
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
87645
|
var SETTINGS_CACHE_TTL_MS = 5e3;
|
|
87165
87646
|
/** TTL for the per-node confirmation-gate detector-step cache (the node's
|
|
87166
87647
|
* default object-detector step rarely changes). */
|
|
@@ -87451,10 +87932,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
87451
87932
|
overlayState = new OverlayDetailStateStore();
|
|
87452
87933
|
/** Throttle state for the "overlay synthesis failed" warn, one per device. */
|
|
87453
87934
|
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
87935
|
/** Frame-based event/track media (crop + boxed full-frame) from the
|
|
87459
87936
|
* detection-pipeline DECODED frame — the ONLY image source (never the
|
|
87460
87937
|
* snapshot cap). Null when shm frame access is unavailable. */
|
|
@@ -91330,6 +91807,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
91330
91807
|
await Promise.all([this.drainSensorHandlers(), this.motionEpisodes?.closeAll("addon-shutdown")]);
|
|
91331
91808
|
await this.embeddingDispatcher?.stop();
|
|
91332
91809
|
this.embeddingDispatcher = null;
|
|
91810
|
+
this.objectEmbeddingStore?.flushRefusals();
|
|
91333
91811
|
await this.notificationCenter?.stop();
|
|
91334
91812
|
this.notificationCenter = null;
|
|
91335
91813
|
for (const id of this.proxies.keys()) this.releaseProxy(id);
|
|
@@ -93012,31 +93490,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
93012
93490
|
});
|
|
93013
93491
|
}
|
|
93014
93492
|
/** CLIP object-embedding detail → the object-embedding store (semantic
|
|
93015
|
-
* search)
|
|
93016
|
-
* gate keeps matching. */
|
|
93493
|
+
* search), under the model the RUNNER stamped — never a guess (D658). */
|
|
93017
93494
|
async routeClipDetail(deviceId, trackId, detail, timestamp) {
|
|
93018
93495
|
const store = this.objectEmbeddingStore;
|
|
93019
93496
|
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
93497
|
const keyFrameMediaId = this.residents.keyFrameMediaId(trackId);
|
|
93032
|
-
await store
|
|
93033
|
-
trackId,
|
|
93498
|
+
await routeClipDetailToStore(store, {
|
|
93034
93499
|
deviceId,
|
|
93500
|
+
trackId,
|
|
93035
93501
|
timestamp,
|
|
93036
93502
|
className: detail.className,
|
|
93037
|
-
|
|
93038
|
-
|
|
93039
|
-
|
|
93503
|
+
score: detail.score,
|
|
93504
|
+
embeddingBase64: detail.embedding,
|
|
93505
|
+
embeddingModelId: detail.embeddingModelId,
|
|
93040
93506
|
...keyFrameMediaId !== void 0 ? { keyFrameMediaId } : {}
|
|
93041
93507
|
});
|
|
93042
93508
|
}
|
|
@@ -93065,23 +93531,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
93065
93531
|
...identityId !== void 0 ? { identityId } : {}
|
|
93066
93532
|
});
|
|
93067
93533
|
}
|
|
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
93534
|
/**
|
|
93086
93535
|
* §5 — decide which active tracks need periodic media THIS frame. Pure over
|
|
93087
93536
|
* TrackStore.lastSnapshotAt + the per-track best-confidence map:
|
|
@@ -93977,6 +94426,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
93977
94426
|
kind: "audio",
|
|
93978
94427
|
rms: input.rms,
|
|
93979
94428
|
dbfs: input.dbfs,
|
|
94429
|
+
classifyOutcome: { state: "classified" },
|
|
93980
94430
|
classification: {
|
|
93981
94431
|
className: input.className,
|
|
93982
94432
|
...input.originalClass !== void 0 ? { originalClass: input.originalClass } : {},
|
|
@@ -94124,11 +94574,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
94124
94574
|
score: topClassification.score
|
|
94125
94575
|
} : {}
|
|
94126
94576
|
}, this.audioMarkerConfig(settings));
|
|
94127
|
-
this.notificationCenter?.observeAudio(deviceId,
|
|
94128
|
-
timestampMs: timestamp,
|
|
94129
|
-
...level?.dbfs !== void 0 ? { dbfs: level.dbfs } : {},
|
|
94130
|
-
labels: (frame.detections ?? []).filter((d) => d.score >= settings.classificationMinScore).map((d) => d.macroClass).filter((macroClass) => settings.audioAllowedClasses.length === 0 || settings.audioAllowedClasses.includes(macroClass))
|
|
94131
|
-
});
|
|
94577
|
+
this.notificationCenter?.observeAudio(deviceId, ncAudioSampleFromFrame(frame, timestamp, settings));
|
|
94132
94578
|
const route = classifyAudioFrame(topClassification ? {
|
|
94133
94579
|
className: topClassification.macroClass,
|
|
94134
94580
|
score: topClassification.score
|
|
@@ -94203,13 +94649,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
94203
94649
|
});
|
|
94204
94650
|
this.levelStateByDevice.set(deviceId, res.nextState);
|
|
94205
94651
|
if (!res.emit) return;
|
|
94206
|
-
const draft =
|
|
94207
|
-
deviceId,
|
|
94208
|
-
timestamp,
|
|
94209
|
-
kind: "audio",
|
|
94210
|
-
rms: level?.rms ?? 0,
|
|
94211
|
-
dbfs: level?.dbfs ?? 0
|
|
94212
|
-
};
|
|
94652
|
+
const draft = levelAudioEventDraft(deviceId, timestamp, frame);
|
|
94213
94653
|
this.lastAudioInsertByDevice.set(deviceId, {
|
|
94214
94654
|
className: void 0,
|
|
94215
94655
|
atMs: timestamp
|
|
@@ -97007,6 +97447,126 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97007
97447
|
}
|
|
97008
97448
|
}
|
|
97009
97449
|
/**
|
|
97450
|
+
* D658: re-embed EXACTLY the named samples with ONE pinned model, forced
|
|
97451
|
+
* (`face-sample-reembed.ts`) — the repair for what the audit accuses. Shares
|
|
97452
|
+
* the gallery-job exclusion with the re-embed pass, the photo pass and the
|
|
97453
|
+
* reset, and reloads the matcher's gallery when anything landed.
|
|
97454
|
+
*/
|
|
97455
|
+
async reembedFaceGallerySamples(input) {
|
|
97456
|
+
const api = this.ctx.api;
|
|
97457
|
+
if (!api) throw new Error("faceGallery.reembedSamples: the addon API is not available");
|
|
97458
|
+
const identityStore = this.identityStore;
|
|
97459
|
+
if (!identityStore) throw new Error("faceGallery.reembedSamples: the identity store is not ready");
|
|
97460
|
+
const mediaStore = this.mediaStore;
|
|
97461
|
+
if (!mediaStore) throw new Error("faceGallery.reembedSamples: the media store is not ready");
|
|
97462
|
+
if (this.faceReembedRunning) throw new Error("faceGallery.reembedSamples: a face re-embed pass is already running");
|
|
97463
|
+
if (this.faceAlignmentJobs?.isPhotoPassRunning === true) throw new Error("faceGallery.reembedSamples: a photo re-enrol pass is running (D643)");
|
|
97464
|
+
if (this.faceAlignmentJobs?.isResetRunning === true) throw new Error("faceGallery.reembedSamples: a face gallery reset is running (D644)");
|
|
97465
|
+
this.faceReembedRunning = true;
|
|
97466
|
+
try {
|
|
97467
|
+
const embedder = new FaceReembedEmbedder({
|
|
97468
|
+
runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
|
|
97469
|
+
logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
|
|
97470
|
+
}, await this.resolveRebuildNodes(api, input.executeOnNodeId), input.modelId);
|
|
97471
|
+
const result = await reembedFaceSamples({
|
|
97472
|
+
listSamples: () => identityStore.listSamplesForReembed(),
|
|
97473
|
+
loadAlignedCrop: async (sample) => {
|
|
97474
|
+
if (sample.mediaId === void 0) return null;
|
|
97475
|
+
try {
|
|
97476
|
+
const file = await mediaStore.getById(sample.mediaId);
|
|
97477
|
+
return file === null ? null : Buffer.from(file.base64, "base64");
|
|
97478
|
+
} catch (err) {
|
|
97479
|
+
this.ctx.logger.warn("face sample re-embed: aligned crop read threw", { meta: {
|
|
97480
|
+
sampleId: sample.sampleId,
|
|
97481
|
+
error: String(err)
|
|
97482
|
+
} });
|
|
97483
|
+
return null;
|
|
97484
|
+
}
|
|
97485
|
+
},
|
|
97486
|
+
embed: (sample, crop) => embedder.embed(sample, crop),
|
|
97487
|
+
store: (sample, embedding, modelId, dim) => identityStore.restampSample({
|
|
97488
|
+
sampleId: sample.sampleId,
|
|
97489
|
+
identityId: sample.identityId,
|
|
97490
|
+
embedding,
|
|
97491
|
+
modelId,
|
|
97492
|
+
dim
|
|
97493
|
+
}),
|
|
97494
|
+
pause: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
97495
|
+
logger: {
|
|
97496
|
+
info: (m, e) => this.ctx.logger.info(m, e),
|
|
97497
|
+
warn: (m, e) => this.ctx.logger.warn(m, e)
|
|
97498
|
+
}
|
|
97499
|
+
}, {
|
|
97500
|
+
sampleIds: input.sampleIds,
|
|
97501
|
+
modelId: input.modelId
|
|
97502
|
+
});
|
|
97503
|
+
if (result.reembedded > 0) await this.faceRecognizer?.refreshGallery();
|
|
97504
|
+
return {
|
|
97505
|
+
...result,
|
|
97506
|
+
rows: [...result.rows]
|
|
97507
|
+
};
|
|
97508
|
+
} finally {
|
|
97509
|
+
this.faceReembedRunning = false;
|
|
97510
|
+
}
|
|
97511
|
+
}
|
|
97512
|
+
/**
|
|
97513
|
+
* D658: re-embed each sample's stored template with ONE pinned model and
|
|
97514
|
+
* compare with its stored vector (`face-embedding-audit.ts`). READ-ONLY —
|
|
97515
|
+
* nothing is restamped, re-embedded or removed. The pass pins the model the
|
|
97516
|
+
* caller names (default `auraface-r100`), not the cluster row: the question
|
|
97517
|
+
* is whether a STAMP is true, whatever the cluster runs today.
|
|
97518
|
+
*/
|
|
97519
|
+
async auditFaceGalleryEmbeddings(input) {
|
|
97520
|
+
const api = this.ctx.api;
|
|
97521
|
+
if (!api) throw new Error("faceGallery.auditEmbeddings: the addon API is not available");
|
|
97522
|
+
const identityStore = this.identityStore;
|
|
97523
|
+
if (!identityStore) throw new Error("faceGallery.auditEmbeddings: the identity store is not ready");
|
|
97524
|
+
const mediaStore = this.mediaStore;
|
|
97525
|
+
if (!mediaStore) throw new Error("faceGallery.auditEmbeddings: the media store is not ready");
|
|
97526
|
+
const auditedModelId = input.modelId ?? "auraface-r100";
|
|
97527
|
+
const embedder = new FaceReembedEmbedder({
|
|
97528
|
+
runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
|
|
97529
|
+
logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
|
|
97530
|
+
}, await this.resolveRebuildNodes(api, input.executeOnNodeId), auditedModelId);
|
|
97531
|
+
const report = await auditFaceEmbeddings({
|
|
97532
|
+
listSamples: () => identityStore.listSamplesForReembed(),
|
|
97533
|
+
loadStoredVectors: (ids) => identityStore.loadSampleVectors(ids),
|
|
97534
|
+
loadAlignedCrop: async (sample) => {
|
|
97535
|
+
if (sample.mediaId === void 0) return null;
|
|
97536
|
+
try {
|
|
97537
|
+
const file = await mediaStore.getById(sample.mediaId);
|
|
97538
|
+
return file === null ? null : Buffer.from(file.base64, "base64");
|
|
97539
|
+
} catch (err) {
|
|
97540
|
+
this.ctx.logger.warn("face embedding audit: aligned crop read threw", { meta: {
|
|
97541
|
+
sampleId: sample.sampleId,
|
|
97542
|
+
error: String(err)
|
|
97543
|
+
} });
|
|
97544
|
+
return null;
|
|
97545
|
+
}
|
|
97546
|
+
},
|
|
97547
|
+
embed: (sample, crop) => embedder.embed(sample, crop)
|
|
97548
|
+
}, {
|
|
97549
|
+
auditedModelId,
|
|
97550
|
+
...input.identityId !== void 0 ? { identityId: input.identityId } : {},
|
|
97551
|
+
...input.maxSamples !== void 0 ? { maxSamples: input.maxSamples } : {}
|
|
97552
|
+
});
|
|
97553
|
+
this.ctx.logger.info("face embedding audit finished", { meta: {
|
|
97554
|
+
auditedModelId,
|
|
97555
|
+
total: report.total,
|
|
97556
|
+
ok: report.ok,
|
|
97557
|
+
mismatch: report.mismatch,
|
|
97558
|
+
inconclusive: report.inconclusive,
|
|
97559
|
+
unmeasurable: report.unmeasurable,
|
|
97560
|
+
complete: report.complete
|
|
97561
|
+
} });
|
|
97562
|
+
return {
|
|
97563
|
+
...report,
|
|
97564
|
+
okMinCosine: AUDIT_OK_MIN_COSINE,
|
|
97565
|
+
mismatchMaxCosine: AUDIT_MISMATCH_MAX_COSINE,
|
|
97566
|
+
rows: [...report.rows]
|
|
97567
|
+
};
|
|
97568
|
+
}
|
|
97569
|
+
/**
|
|
97010
97570
|
* The server-side half of "change the model from any UI and the gallery
|
|
97011
97571
|
* follows": if a sample sits outside the cluster space and a pass could
|
|
97012
97572
|
* still bring it in, start one. Runs on a timer; converges and goes quiet.
|
|
@@ -97529,6 +98089,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
97529
98089
|
samples: [...result.samples]
|
|
97530
98090
|
};
|
|
97531
98091
|
},
|
|
98092
|
+
"faceGallery.reembedSamples": async (input) => this.reembedFaceGallerySamples(input),
|
|
98093
|
+
"faceGallery.auditEmbeddings": async (input) => this.auditFaceGalleryEmbeddings(input),
|
|
97532
98094
|
"faceGallery.sourceFrameStats": async () => ({ devices: this.faceSourceStats.snapshot().map((d) => ({
|
|
97533
98095
|
...d,
|
|
97534
98096
|
dropped: { ...d.dropped }
|