@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as NcRuleSchema, $n as EventCategory, $t as isSourceCap, A as LabelAttributionSchema, An as canonicalHash, At as audioMetricsCapability, B as NC_DEFAULT_SNOOZE_MINUTES, Bt as embeddingRefusalOf, C as EMBEDDING_REQUIREMENTS_FAILED, Cn as systemEventFilterApplies, Ct as TimelapseRuleInputSchema, D as FIRST_LEVEL_MACRO_CLASSES, Dn as vectorDimFromBase64, Dt as addonWidgetsSourceCapability, E as EVENT_OWNER_TYPES, En as templateVarsFor, Et as TrackSourceSchema, F as MOTION_CLOSE_AFTER_MS, Fn as customAction, Ft as deriveRecordingMode, G as NC_TAXONOMY, Gn as discriminatedUnion, Gt as evictionPolicyOfLocation, Hn as _enum, I as MediaFileKindEnum, In as defineCustomActions, It as describeSystemEventDeliveryProblem, J as NcAlarmSettingsPatchSchema, Jn as object, Jt as isDetectionMacroClass, K as NC_TEMPLATE_VARS, Kn as literal, Kt as faceGalleryCapability, L as NC_ALARM_SYSTEM_EVENT_KINDS, Ln as hydrateSchema, Lt as describeSystemEventFamilyProblem, M as MAX_ARCHIVED_DEBUG_NOTES, Mn as CamProfileSchema, Mt as buildEventKindDescriptor, N as MAX_BIRTH_DECISION_RECORDS, Nn as DeviceType, Nt as consumableLowPercentOf, O as FULL_IMAGE_BBOX, On as zoneAnalyticsCapability, Ot as alarmPanelCapability, Pn as createEvent, Pt as cosineSimilarity$1, Q as NcRulePatchSchema, Qn as unknown, Qt as isScheduleActive, R as NC_CONDITION_CATALOG, Rn as isDeviceScopedCap, S as EMBEDDING_MODEL_FAILED, Sn as systemEventFamilyProblems, St as TIMELAPSE_DENSE_FLOOR_SEC, T as EVENT_KIND_BY_CAP, Tn as templateCarriesSummary, Tt as TimelapseRuleSchema, U as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, Un as array, Ut as encodeVectorBase64, V as NC_SENSOR_TRANSITION_KINDS, Vn as sleep$1, W as NC_TANK_SYSTEM_EVENT_KINDS, Wn as boolean, Wt as evaluateSensorEdge, X as NcConditionsSchema, Xn as record, Xt as isKnownTemplateVar, Y as NcConditionDescriptorSchema, Yn as partialRecord, Yt as isEventOwnerType, Z as NcRuleInputSchema, Zn as string, Zt as isMediaOwnerType, _n as sensorRaisedOf, a as CLIP_EMBEDDING_STEP_ID, an as notificationRulesCapability, at as NcSnoozeSuppressedSchema, bn as subKindsOf, bt as SCENE_DIVERGED, c as resolveClusterModelPin, cn as pipelineAnalyticsCapability, ct as NcTemplatePreviewInputSchema, d as ArchivedDebugNoteSchema, dn as readTimelapseGeneratedAt, dt as OpsLogEntrySchema, en as isSystemDelivery, et as NcRuleTargetSchema, f as BaseDevice, fn as resolveClipSearchMinScore, g as DEFAULT_EVENT_COLOR, gn as sceneMonitorCapability, gt as RetrainStatusSchema, h as CoverStateSchema, hn as ruleBelongsToAlarm$1, ht as RebuildObjectEmbeddingsRefusalSchema, in as mayWriteToLocation, it as NcSnoozeSchema, j as LockStateSchema, jn as BaseAddon, jt as audioModeOf, k as FailureCounters, kn as errMsg$1, kt as assertTimelapseCadences, l as AUDIO_MACRO_LABELS, ln as plateGalleryCapability, lt as NcTemplatePreviewSchema, m as COCO_TO_MACRO, mt as RECORDING_SOURCE_CAMSTACK_ADDON, nn as isVectorIndexDimConflict, nt as NcScheduleSchema, o as FACE_EMBEDDING_STEP_ID, on as occupancyScope, ot as NcSystemEventKindSchema, p as BirthDecisionRecordSchema, pn as resolveLocationMode, pt as RECORDING_EXPORT_MAX_READ_BYTES, q as NcAlarmConfigSchema, qn as number, qt as failureContributionCapability, rn as kebabToCamel, rt as NcSnoozeInputSchema, s as readClusterModelPinStrict, st as NcTaxonomySchema, t as BUILTIN_CLIP_MODELS, tn as isSystemEventSummaryRule, tt as NcRuleUpdateInputSchema, u as AlarmArmModeSchema, un as readDeviceStateFrom, ut as NcTemplateVarDescriptorSchema, vn as servedTemplateVars, vt as SCENE_DEFAULT_ANCHOR_THRESHOLD, w as EMBEDDING_REQUIREMENTS_UNAVAILABLE, wn as systemEventSummaryScheduleOf, wt as TimelapseRulePatchSchema, x as DeclaredDevices, xn as systemEventDeliveryProblems, xt as SceneMonitorSchema, y as DETECTION_MACRO_CLASSES, yn as storageOccupancyCapability, yt as SCENE_DEFAULT_UNCOVERED_POLICY, z as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, zn as nodePin } from "../clip-model-registry-
|
|
1
|
+
import { $ as NcRuleSchema, $n as EventCategory, $t as isSourceCap, A as LabelAttributionSchema, An as canonicalHash, At as audioMetricsCapability, B as NC_DEFAULT_SNOOZE_MINUTES, Bt as embeddingRefusalOf, C as EMBEDDING_REQUIREMENTS_FAILED, Cn as systemEventFilterApplies, Ct as TimelapseRuleInputSchema, D as FIRST_LEVEL_MACRO_CLASSES, Dn as vectorDimFromBase64, Dt as addonWidgetsSourceCapability, E as EVENT_OWNER_TYPES, En as templateVarsFor, Et as TrackSourceSchema, F as MOTION_CLOSE_AFTER_MS, Fn as customAction, Ft as deriveRecordingMode, G as NC_TAXONOMY, Gn as discriminatedUnion, Gt as evictionPolicyOfLocation, Hn as _enum, I as MediaFileKindEnum, In as defineCustomActions, It as describeSystemEventDeliveryProblem, J as NcAlarmSettingsPatchSchema, Jn as object, Jt as isDetectionMacroClass, K as NC_TEMPLATE_VARS, Kn as literal, Kt as faceGalleryCapability, L as NC_ALARM_SYSTEM_EVENT_KINDS, Ln as hydrateSchema, Lt as describeSystemEventFamilyProblem, M as MAX_ARCHIVED_DEBUG_NOTES, Mn as CamProfileSchema, Mt as buildEventKindDescriptor, N as MAX_BIRTH_DECISION_RECORDS, Nn as DeviceType, Nt as consumableLowPercentOf, O as FULL_IMAGE_BBOX, On as zoneAnalyticsCapability, Ot as alarmPanelCapability, Pn as createEvent, Pt as cosineSimilarity$1, Q as NcRulePatchSchema, Qn as unknown, Qt as isScheduleActive, R as NC_CONDITION_CATALOG, Rn as isDeviceScopedCap, S as EMBEDDING_MODEL_FAILED, Sn as systemEventFamilyProblems, St as TIMELAPSE_DENSE_FLOOR_SEC, T as EVENT_KIND_BY_CAP, Tn as templateCarriesSummary, Tt as TimelapseRuleSchema, U as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, Un as array, Ut as encodeVectorBase64, V as NC_SENSOR_TRANSITION_KINDS, Vn as sleep$1, W as NC_TANK_SYSTEM_EVENT_KINDS, Wn as boolean, Wt as evaluateSensorEdge, X as NcConditionsSchema, Xn as record, Xt as isKnownTemplateVar, Y as NcConditionDescriptorSchema, Yn as partialRecord, Yt as isEventOwnerType, Z as NcRuleInputSchema, Zn as string, Zt as isMediaOwnerType, _n as sensorRaisedOf, a as CLIP_EMBEDDING_STEP_ID, an as notificationRulesCapability, at as NcSnoozeSuppressedSchema, bn as subKindsOf, bt as SCENE_DIVERGED, c as resolveClusterModelPin, cn as pipelineAnalyticsCapability, ct as NcTemplatePreviewInputSchema, d as ArchivedDebugNoteSchema, dn as readTimelapseGeneratedAt, dt as OpsLogEntrySchema, en as isSystemDelivery, et as NcRuleTargetSchema, f as BaseDevice, fn as resolveClipSearchMinScore, g as DEFAULT_EVENT_COLOR, gn as sceneMonitorCapability, gt as RetrainStatusSchema, h as CoverStateSchema, hn as ruleBelongsToAlarm$1, ht as RebuildObjectEmbeddingsRefusalSchema, in as mayWriteToLocation, it as NcSnoozeSchema, j as LockStateSchema, jn as BaseAddon, jt as audioModeOf, k as FailureCounters, kn as errMsg$1, kt as assertTimelapseCadences, l as AUDIO_MACRO_LABELS, ln as plateGalleryCapability, lt as NcTemplatePreviewSchema, m as COCO_TO_MACRO, mt as RECORDING_SOURCE_CAMSTACK_ADDON, nn as isVectorIndexDimConflict, nt as NcScheduleSchema, o as FACE_EMBEDDING_STEP_ID, on as occupancyScope, ot as NcSystemEventKindSchema, p as BirthDecisionRecordSchema, pn as resolveLocationMode, pt as RECORDING_EXPORT_MAX_READ_BYTES, q as NcAlarmConfigSchema, qn as number, qt as failureContributionCapability, rn as kebabToCamel, rt as NcSnoozeInputSchema, s as readClusterModelPinStrict, st as NcTaxonomySchema, t as BUILTIN_CLIP_MODELS, tn as isSystemEventSummaryRule, tt as NcRuleUpdateInputSchema, u as AlarmArmModeSchema, un as readDeviceStateFrom, ut as NcTemplateVarDescriptorSchema, vn as servedTemplateVars, vt as SCENE_DEFAULT_ANCHOR_THRESHOLD, w as EMBEDDING_REQUIREMENTS_UNAVAILABLE, wn as systemEventSummaryScheduleOf, wt as TimelapseRulePatchSchema, x as DeclaredDevices, xn as systemEventDeliveryProblems, xt as SceneMonitorSchema, y as DETECTION_MACRO_CLASSES, yn as storageOccupancyCapability, yt as SCENE_DEFAULT_UNCOVERED_POLICY, z as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, zn as nodePin } from "../clip-model-registry-BRHeTbFV.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { promises } from "node:fs";
|
|
@@ -82,7 +82,7 @@ function motionEpisodeDurationMs(state) {
|
|
|
82
82
|
}
|
|
83
83
|
//#endregion
|
|
84
84
|
//#region src/pipeline-analytics/motion-episode-tracker.ts
|
|
85
|
-
function keyOf$
|
|
85
|
+
function keyOf$2(deviceId, source) {
|
|
86
86
|
return `${deviceId}:${source}`;
|
|
87
87
|
}
|
|
88
88
|
var MotionEpisodeTracker = class {
|
|
@@ -98,7 +98,7 @@ var MotionEpisodeTracker = class {
|
|
|
98
98
|
* {@link MotionEpisodeInsertInput}.
|
|
99
99
|
*/
|
|
100
100
|
async observe(deviceId, source, detected, timestamp, regionData) {
|
|
101
|
-
const key = keyOf$
|
|
101
|
+
const key = keyOf$2(deviceId, source);
|
|
102
102
|
const prior = this.signals.get(key);
|
|
103
103
|
const decision = decideMotionSignal(prior, detected, timestamp);
|
|
104
104
|
switch (decision.kind) {
|
|
@@ -12773,14 +12773,14 @@ var NcPruneRecordSchema = object({
|
|
|
12773
12773
|
});
|
|
12774
12774
|
/** Rules × deleted subjects — small; a generous ceiling. */
|
|
12775
12775
|
var LOAD_LIMIT$4 = 1e4;
|
|
12776
|
-
function keyOf(row) {
|
|
12776
|
+
function keyOf$1(row) {
|
|
12777
12777
|
return `${row.kind}:${row.ruleId}:${row.field}:${row.subject}`;
|
|
12778
12778
|
}
|
|
12779
12779
|
var NC_DEVICE_PRUNES_SPEC = {
|
|
12780
12780
|
collection: NC_DEVICE_PRUNES_COLLECTION,
|
|
12781
12781
|
columns: [...NC_DEVICE_PRUNES_COLUMNS],
|
|
12782
12782
|
writeMode: "write-through",
|
|
12783
|
-
keyOf,
|
|
12783
|
+
keyOf: keyOf$1,
|
|
12784
12784
|
toValue: (row) => ({
|
|
12785
12785
|
ruleId: row.ruleId,
|
|
12786
12786
|
at: row.at,
|
|
@@ -12820,7 +12820,7 @@ var NcPruneLedger = class {
|
|
|
12820
12820
|
* {@link isLoaded} as it was — see the module docblock.
|
|
12821
12821
|
*/
|
|
12822
12822
|
async load() {
|
|
12823
|
-
const heldBefore = new Set(this.ledger.snapshot().map(keyOf));
|
|
12823
|
+
const heldBefore = new Set(this.ledger.snapshot().map(keyOf$1));
|
|
12824
12824
|
const startedAt = this.writes;
|
|
12825
12825
|
const writtenSince = (key) => (this.writtenAt.get(key) ?? 0) > startedAt;
|
|
12826
12826
|
try {
|
|
@@ -12831,7 +12831,7 @@ var NcPruneLedger = class {
|
|
|
12831
12831
|
const next = /* @__PURE__ */ new Map();
|
|
12832
12832
|
for (const record of records) {
|
|
12833
12833
|
const row = NC_DEVICE_PRUNES_SPEC.fromRecord(record.id, record.data);
|
|
12834
|
-
if (row !== null) next.set(keyOf(row), row);
|
|
12834
|
+
if (row !== null) next.set(keyOf$1(row), row);
|
|
12835
12835
|
}
|
|
12836
12836
|
for (const key of heldBefore) if (!next.has(key) && !writtenSince(key)) this.ledger.evict(key);
|
|
12837
12837
|
for (const [key, row] of next) if (!writtenSince(key)) this.ledger.stage(row);
|
|
@@ -12861,7 +12861,7 @@ var NcPruneLedger = class {
|
|
|
12861
12861
|
*/
|
|
12862
12862
|
async record(notes, source, at) {
|
|
12863
12863
|
for (const note of notes) {
|
|
12864
|
-
const held = this.ledger.get(keyOf(note));
|
|
12864
|
+
const held = this.ledger.get(keyOf$1(note));
|
|
12865
12865
|
if (held !== void 0 && KEPT_OUTCOMES.has(held.outcome) && !KEPT_OUTCOMES.has(note.outcome)) continue;
|
|
12866
12866
|
const row = NcPruneRecordSchema.parse({
|
|
12867
12867
|
...note,
|
|
@@ -12869,7 +12869,7 @@ var NcPruneLedger = class {
|
|
|
12869
12869
|
source
|
|
12870
12870
|
});
|
|
12871
12871
|
this.writes += 1;
|
|
12872
|
-
this.writtenAt.set(keyOf(row), this.writes);
|
|
12872
|
+
this.writtenAt.set(keyOf$1(row), this.writes);
|
|
12873
12873
|
await this.ledger.put(row);
|
|
12874
12874
|
}
|
|
12875
12875
|
}
|
|
@@ -39685,6 +39685,87 @@ var ReembedFacesInputSchema = object({
|
|
|
39685
39685
|
/** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
|
|
39686
39686
|
executeOnNodeId: string().optional()
|
|
39687
39687
|
});
|
|
39688
|
+
/**
|
|
39689
|
+
* D658: re-embed each sample's stored template with ONE pinned model and
|
|
39690
|
+
* compare with the stored vector — does the stamp say what produced it?
|
|
39691
|
+
* Read-only: it reports and writes nothing.
|
|
39692
|
+
*/
|
|
39693
|
+
var AuditFaceEmbeddingsInputSchema = object({
|
|
39694
|
+
/** The stamp to verify. Omit for `auraface-r100` (the D651 default). */
|
|
39695
|
+
modelId: string().min(1).optional(),
|
|
39696
|
+
/** Only this identity's samples. */
|
|
39697
|
+
identityId: string().min(1).optional(),
|
|
39698
|
+
/** Stop after this many measured samples. */
|
|
39699
|
+
maxSamples: number().int().positive().optional(),
|
|
39700
|
+
/** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
|
|
39701
|
+
executeOnNodeId: string().optional()
|
|
39702
|
+
});
|
|
39703
|
+
var AuditFaceEmbeddingsResultSchema = object({
|
|
39704
|
+
auditedModelId: string(),
|
|
39705
|
+
total: number(),
|
|
39706
|
+
ok: number(),
|
|
39707
|
+
mismatch: number(),
|
|
39708
|
+
inconclusive: number(),
|
|
39709
|
+
unmeasurable: number(),
|
|
39710
|
+
complete: boolean(),
|
|
39711
|
+
okMinCosine: number(),
|
|
39712
|
+
mismatchMaxCosine: number(),
|
|
39713
|
+
rows: array(object({
|
|
39714
|
+
sampleId: string(),
|
|
39715
|
+
identityId: string(),
|
|
39716
|
+
identityName: string(),
|
|
39717
|
+
stampedModelId: string(),
|
|
39718
|
+
verdict: _enum([
|
|
39719
|
+
"ok",
|
|
39720
|
+
"mismatch",
|
|
39721
|
+
"inconclusive",
|
|
39722
|
+
"unmeasurable"
|
|
39723
|
+
]),
|
|
39724
|
+
cosine: number().optional(),
|
|
39725
|
+
reason: _enum([
|
|
39726
|
+
"stamped-other-model",
|
|
39727
|
+
"no-pixels",
|
|
39728
|
+
"no-stored-vector",
|
|
39729
|
+
"embed-failed",
|
|
39730
|
+
"no-capable-node",
|
|
39731
|
+
"dimension-differs"
|
|
39732
|
+
]).optional(),
|
|
39733
|
+
detail: string().optional()
|
|
39734
|
+
}))
|
|
39735
|
+
});
|
|
39736
|
+
/**
|
|
39737
|
+
* D658: re-embed EXACTLY these samples with ONE pinned model, replacing their
|
|
39738
|
+
* vectors even when already stamped with it — the repair for what
|
|
39739
|
+
* `faceGallery.auditEmbeddings` accuses. See `face-sample-reembed.ts`.
|
|
39740
|
+
*/
|
|
39741
|
+
var ReembedSamplesInputSchema = object({
|
|
39742
|
+
sampleIds: array(string().min(1)).min(1).max(200),
|
|
39743
|
+
modelId: string().min(1),
|
|
39744
|
+
/** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
|
|
39745
|
+
executeOnNodeId: string().optional()
|
|
39746
|
+
});
|
|
39747
|
+
var ReembedSamplesResultSchema = object({
|
|
39748
|
+
pinnedModelId: string(),
|
|
39749
|
+
requested: number(),
|
|
39750
|
+
reembedded: number(),
|
|
39751
|
+
failed: number(),
|
|
39752
|
+
rows: array(object({
|
|
39753
|
+
sampleId: string(),
|
|
39754
|
+
outcome: _enum([
|
|
39755
|
+
"reembedded",
|
|
39756
|
+
"not-found",
|
|
39757
|
+
"duplicate",
|
|
39758
|
+
"no-pixels",
|
|
39759
|
+
"embed-failed",
|
|
39760
|
+
"no-capable-node",
|
|
39761
|
+
"no-model-stamp",
|
|
39762
|
+
"not-landed"
|
|
39763
|
+
]),
|
|
39764
|
+
previousModelId: string().optional(),
|
|
39765
|
+
modelId: string().optional(),
|
|
39766
|
+
detail: string().optional()
|
|
39767
|
+
}))
|
|
39768
|
+
});
|
|
39688
39769
|
/** Mirrors `FaceReembedResult` (`face-reembed-pass.ts`) minus the sample-id set. */
|
|
39689
39770
|
var ReembedFacesResultSchema = object({
|
|
39690
39771
|
targetModelId: string(),
|
|
@@ -39936,11 +40017,256 @@ var embeddingActions = defineCustomActions({
|
|
|
39936
40017
|
"embedding.faceAlignmentStatus": customAction(NoInputSchema$1, FaceAlignmentStatusSchema),
|
|
39937
40018
|
"embedding.backfillPhotoSources": customAction(BackfillPhotoSourcesInputSchema, BackfillPhotoSourcesResultSchema, { auth: "admin" }),
|
|
39938
40019
|
"faceGallery.sourceFrameStats": customAction(NoInputSchema$1, SourceFrameStatsSchema),
|
|
40020
|
+
"faceGallery.reembedSamples": customAction(ReembedSamplesInputSchema, ReembedSamplesResultSchema, {
|
|
40021
|
+
kind: "mutation",
|
|
40022
|
+
auth: "admin"
|
|
40023
|
+
}),
|
|
40024
|
+
"faceGallery.auditEmbeddings": customAction(AuditFaceEmbeddingsInputSchema, AuditFaceEmbeddingsResultSchema, { auth: "admin" }),
|
|
39939
40025
|
"faceGallery.resetToCore": customAction(ResetToCoreInputSchema, ResetToCoreResultSchema, {
|
|
39940
40026
|
kind: "mutation",
|
|
39941
40027
|
auth: "admin"
|
|
39942
40028
|
})
|
|
39943
40029
|
});
|
|
40030
|
+
async function reembedOne(deps, sample, pinned) {
|
|
40031
|
+
const base = {
|
|
40032
|
+
sampleId: sample.sampleId,
|
|
40033
|
+
previousModelId: sample.modelId
|
|
40034
|
+
};
|
|
40035
|
+
const crop = await deps.loadAlignedCrop(sample);
|
|
40036
|
+
if (crop === null) return {
|
|
40037
|
+
...base,
|
|
40038
|
+
outcome: "no-pixels"
|
|
40039
|
+
};
|
|
40040
|
+
const outcome = await deps.embed(sample, crop);
|
|
40041
|
+
if (outcome.kind === "failed") return {
|
|
40042
|
+
...base,
|
|
40043
|
+
outcome: "embed-failed",
|
|
40044
|
+
detail: outcome.reason
|
|
40045
|
+
};
|
|
40046
|
+
if (outcome.kind === "no-capable-node") return {
|
|
40047
|
+
...base,
|
|
40048
|
+
outcome: "no-capable-node",
|
|
40049
|
+
detail: outcome.reason
|
|
40050
|
+
};
|
|
40051
|
+
if (outcome.modelId === void 0 || outcome.modelId === "") return {
|
|
40052
|
+
...base,
|
|
40053
|
+
outcome: "no-model-stamp",
|
|
40054
|
+
detail: "the embed answered no model id"
|
|
40055
|
+
};
|
|
40056
|
+
if (outcome.modelId !== pinned) return {
|
|
40057
|
+
...base,
|
|
40058
|
+
outcome: "embed-failed",
|
|
40059
|
+
detail: `ran "${outcome.modelId}" for a call pinned to "${pinned}"`
|
|
40060
|
+
};
|
|
40061
|
+
const write = await deps.store(sample, outcome.embedding, outcome.modelId, outcome.dim);
|
|
40062
|
+
if (!write.landed) return {
|
|
40063
|
+
...base,
|
|
40064
|
+
outcome: "not-landed",
|
|
40065
|
+
detail: write.reason ?? "the write did not land"
|
|
40066
|
+
};
|
|
40067
|
+
return {
|
|
40068
|
+
...base,
|
|
40069
|
+
outcome: "reembedded",
|
|
40070
|
+
modelId: outcome.modelId
|
|
40071
|
+
};
|
|
40072
|
+
}
|
|
40073
|
+
async function reembedFaceSamples(deps, options) {
|
|
40074
|
+
const pinned = options.modelId;
|
|
40075
|
+
const byId = new Map((await deps.listSamples()).map((s) => [s.sampleId, s]));
|
|
40076
|
+
const pacingMs = options.pacingMs ?? 25;
|
|
40077
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40078
|
+
const rows = [];
|
|
40079
|
+
let noNode = null;
|
|
40080
|
+
deps.logger.info("face sample re-embed starting", { meta: {
|
|
40081
|
+
pinnedModelId: pinned,
|
|
40082
|
+
requested: options.sampleIds.length
|
|
40083
|
+
} });
|
|
40084
|
+
for (const sampleId of options.sampleIds) {
|
|
40085
|
+
if (seen.has(sampleId)) {
|
|
40086
|
+
rows.push({
|
|
40087
|
+
sampleId,
|
|
40088
|
+
outcome: "duplicate"
|
|
40089
|
+
});
|
|
40090
|
+
continue;
|
|
40091
|
+
}
|
|
40092
|
+
seen.add(sampleId);
|
|
40093
|
+
const sample = byId.get(sampleId);
|
|
40094
|
+
if (sample === void 0) {
|
|
40095
|
+
rows.push({
|
|
40096
|
+
sampleId,
|
|
40097
|
+
outcome: "not-found"
|
|
40098
|
+
});
|
|
40099
|
+
continue;
|
|
40100
|
+
}
|
|
40101
|
+
if (noNode !== null) {
|
|
40102
|
+
rows.push({
|
|
40103
|
+
sampleId,
|
|
40104
|
+
previousModelId: sample.modelId,
|
|
40105
|
+
outcome: "no-capable-node",
|
|
40106
|
+
detail: noNode
|
|
40107
|
+
});
|
|
40108
|
+
continue;
|
|
40109
|
+
}
|
|
40110
|
+
const row = await reembedOne(deps, sample, pinned);
|
|
40111
|
+
if (row.outcome === "no-capable-node") noNode = row.detail ?? "no capable node";
|
|
40112
|
+
if (row.outcome !== "reembedded") deps.logger.warn("face sample re-embed: sample NOT re-embedded", { meta: {
|
|
40113
|
+
sampleId,
|
|
40114
|
+
outcome: row.outcome,
|
|
40115
|
+
detail: row.detail ?? null,
|
|
40116
|
+
pinnedModelId: pinned
|
|
40117
|
+
} });
|
|
40118
|
+
rows.push(row);
|
|
40119
|
+
await deps.pause(pacingMs);
|
|
40120
|
+
}
|
|
40121
|
+
const reembedded = rows.filter((r) => r.outcome === "reembedded").length;
|
|
40122
|
+
deps.logger.info("face sample re-embed finished", { meta: {
|
|
40123
|
+
pinnedModelId: pinned,
|
|
40124
|
+
requested: options.sampleIds.length,
|
|
40125
|
+
reembedded,
|
|
40126
|
+
failed: rows.length - reembedded
|
|
40127
|
+
} });
|
|
40128
|
+
return {
|
|
40129
|
+
pinnedModelId: pinned,
|
|
40130
|
+
requested: options.sampleIds.length,
|
|
40131
|
+
reembedded,
|
|
40132
|
+
failed: rows.length - reembedded,
|
|
40133
|
+
rows
|
|
40134
|
+
};
|
|
40135
|
+
}
|
|
40136
|
+
//#endregion
|
|
40137
|
+
//#region src/pipeline-analytics/face-embedding-audit.ts
|
|
40138
|
+
/** At or above: the stored vector is this model's. */
|
|
40139
|
+
var AUDIT_OK_MIN_COSINE = .98;
|
|
40140
|
+
/** Below: the stored vector is NOT this model's. Between the two: inconclusive. */
|
|
40141
|
+
var AUDIT_MISMATCH_MAX_COSINE = .9;
|
|
40142
|
+
/** Cosine of two vectors of equal length; `null` when either has no length. */
|
|
40143
|
+
function cosine(a, b) {
|
|
40144
|
+
let dot = 0;
|
|
40145
|
+
let na = 0;
|
|
40146
|
+
let nb = 0;
|
|
40147
|
+
for (let i = 0; i < a.length; i++) {
|
|
40148
|
+
const x = a[i] ?? 0;
|
|
40149
|
+
const y = b[i] ?? 0;
|
|
40150
|
+
dot += x * y;
|
|
40151
|
+
na += x * x;
|
|
40152
|
+
nb += y * y;
|
|
40153
|
+
}
|
|
40154
|
+
if (na === 0 || nb === 0) return null;
|
|
40155
|
+
return dot / Math.sqrt(na * nb);
|
|
40156
|
+
}
|
|
40157
|
+
/** The verdict a measured cosine earns. Pure. */
|
|
40158
|
+
function verdictOf(c) {
|
|
40159
|
+
if (c >= .98) return "ok";
|
|
40160
|
+
if (c < .9) return "mismatch";
|
|
40161
|
+
return "inconclusive";
|
|
40162
|
+
}
|
|
40163
|
+
function rowBase(s) {
|
|
40164
|
+
return {
|
|
40165
|
+
sampleId: s.sampleId,
|
|
40166
|
+
identityId: s.identityId,
|
|
40167
|
+
identityName: s.identityName,
|
|
40168
|
+
stampedModelId: s.modelId
|
|
40169
|
+
};
|
|
40170
|
+
}
|
|
40171
|
+
async function measure$2(deps, sample, stored) {
|
|
40172
|
+
const base = rowBase(sample);
|
|
40173
|
+
if (stored === void 0) return {
|
|
40174
|
+
...base,
|
|
40175
|
+
verdict: "unmeasurable",
|
|
40176
|
+
reason: "no-stored-vector"
|
|
40177
|
+
};
|
|
40178
|
+
const crop = await deps.loadAlignedCrop(sample);
|
|
40179
|
+
if (crop === null) return {
|
|
40180
|
+
...base,
|
|
40181
|
+
verdict: "unmeasurable",
|
|
40182
|
+
reason: "no-pixels"
|
|
40183
|
+
};
|
|
40184
|
+
const outcome = await deps.embed(sample, crop);
|
|
40185
|
+
if (outcome.kind === "failed") return {
|
|
40186
|
+
...base,
|
|
40187
|
+
verdict: "unmeasurable",
|
|
40188
|
+
reason: "embed-failed",
|
|
40189
|
+
detail: outcome.reason
|
|
40190
|
+
};
|
|
40191
|
+
if (outcome.kind === "no-capable-node") return {
|
|
40192
|
+
...base,
|
|
40193
|
+
verdict: "unmeasurable",
|
|
40194
|
+
reason: "no-capable-node",
|
|
40195
|
+
detail: outcome.reason
|
|
40196
|
+
};
|
|
40197
|
+
if (outcome.embedding.length !== stored.length) return {
|
|
40198
|
+
...base,
|
|
40199
|
+
verdict: "unmeasurable",
|
|
40200
|
+
reason: "dimension-differs",
|
|
40201
|
+
detail: `stored ${stored.length}-d, re-embedded ${outcome.embedding.length}-d`
|
|
40202
|
+
};
|
|
40203
|
+
const c = cosine(outcome.embedding, stored);
|
|
40204
|
+
if (c === null) return {
|
|
40205
|
+
...base,
|
|
40206
|
+
verdict: "unmeasurable",
|
|
40207
|
+
reason: "no-stored-vector",
|
|
40208
|
+
detail: "zero vector"
|
|
40209
|
+
};
|
|
40210
|
+
return {
|
|
40211
|
+
...base,
|
|
40212
|
+
verdict: verdictOf(c),
|
|
40213
|
+
cosine: Math.round(c * 1e4) / 1e4
|
|
40214
|
+
};
|
|
40215
|
+
}
|
|
40216
|
+
/**
|
|
40217
|
+
* Audit every sample stamped with `auditedModelId`. Read-only; bounded by
|
|
40218
|
+
* `maxSamples`; a pass-terminal `no-capable-node` stops measuring and reports
|
|
40219
|
+
* the rest as unmeasurable rather than asking again per sample.
|
|
40220
|
+
*/
|
|
40221
|
+
async function auditFaceEmbeddings(deps, options) {
|
|
40222
|
+
const all = await deps.listSamples();
|
|
40223
|
+
const samples = options.identityId === void 0 ? all : all.filter((s) => s.identityId === options.identityId);
|
|
40224
|
+
const inSpace = samples.filter((s) => s.modelId === options.auditedModelId);
|
|
40225
|
+
const stored = await deps.loadStoredVectors(inSpace.map((s) => s.sampleId));
|
|
40226
|
+
const budget = options.maxSamples ?? Number.POSITIVE_INFINITY;
|
|
40227
|
+
const rows = [];
|
|
40228
|
+
let measured = 0;
|
|
40229
|
+
let complete = true;
|
|
40230
|
+
let noNode = null;
|
|
40231
|
+
for (const sample of samples) {
|
|
40232
|
+
if (sample.modelId !== options.auditedModelId) {
|
|
40233
|
+
rows.push({
|
|
40234
|
+
...rowBase(sample),
|
|
40235
|
+
verdict: "unmeasurable",
|
|
40236
|
+
reason: "stamped-other-model"
|
|
40237
|
+
});
|
|
40238
|
+
continue;
|
|
40239
|
+
}
|
|
40240
|
+
if (measured >= budget) {
|
|
40241
|
+
complete = false;
|
|
40242
|
+
continue;
|
|
40243
|
+
}
|
|
40244
|
+
if (noNode !== null) {
|
|
40245
|
+
rows.push({
|
|
40246
|
+
...rowBase(sample),
|
|
40247
|
+
verdict: "unmeasurable",
|
|
40248
|
+
reason: "no-capable-node",
|
|
40249
|
+
detail: noNode
|
|
40250
|
+
});
|
|
40251
|
+
continue;
|
|
40252
|
+
}
|
|
40253
|
+
const row = await measure$2(deps, sample, stored.get(sample.sampleId));
|
|
40254
|
+
if (row.reason === "no-capable-node") noNode = row.detail ?? "no capable node";
|
|
40255
|
+
if (row.cosine !== void 0) measured += 1;
|
|
40256
|
+
rows.push(row);
|
|
40257
|
+
}
|
|
40258
|
+
const count = (v) => rows.filter((r) => r.verdict === v).length;
|
|
40259
|
+
return {
|
|
40260
|
+
auditedModelId: options.auditedModelId,
|
|
40261
|
+
total: samples.length,
|
|
40262
|
+
ok: count("ok"),
|
|
40263
|
+
mismatch: count("mismatch"),
|
|
40264
|
+
inconclusive: count("inconclusive"),
|
|
40265
|
+
unmeasurable: count("unmeasurable"),
|
|
40266
|
+
complete,
|
|
40267
|
+
rows
|
|
40268
|
+
};
|
|
40269
|
+
}
|
|
39944
40270
|
//#endregion
|
|
39945
40271
|
//#region src/pipeline-analytics/face-alignment-version.ts
|
|
39946
40272
|
/**
|
|
@@ -65931,7 +66257,8 @@ var FaceReembedEmbedder = class {
|
|
|
65931
66257
|
return {
|
|
65932
66258
|
kind: "embedded",
|
|
65933
66259
|
embedding: vector,
|
|
65934
|
-
dim: vector.length
|
|
66260
|
+
dim: vector.length,
|
|
66261
|
+
modelId: result.modelId
|
|
65935
66262
|
};
|
|
65936
66263
|
}
|
|
65937
66264
|
}
|
|
@@ -84222,6 +84549,79 @@ async function resolveSubjectShot(ports, trackId, deviceId, aroundMs) {
|
|
|
84222
84549
|
frameHeight: dims.h
|
|
84223
84550
|
};
|
|
84224
84551
|
}
|
|
84552
|
+
var MESSAGES = {
|
|
84553
|
+
"dimension-mismatch": "object embeddings refused — their length is not their model's dimension",
|
|
84554
|
+
"no-clip-metadata": "object embeddings refused — the model has no CLIP metadata",
|
|
84555
|
+
"no-model-id": "object embeddings refused — the result names no model"
|
|
84556
|
+
};
|
|
84557
|
+
function keyOf(r) {
|
|
84558
|
+
return `${String(r.deviceId)}|${r.modelId ?? ""}|${r.reason}`;
|
|
84559
|
+
}
|
|
84560
|
+
var EmbeddingRefusalReport = class {
|
|
84561
|
+
logger;
|
|
84562
|
+
intervalMs;
|
|
84563
|
+
now;
|
|
84564
|
+
keys = /* @__PURE__ */ new Map();
|
|
84565
|
+
constructor(options) {
|
|
84566
|
+
this.logger = options.logger;
|
|
84567
|
+
this.intervalMs = options.intervalMs ?? 3e5;
|
|
84568
|
+
this.now = options.now ?? (() => Date.now());
|
|
84569
|
+
}
|
|
84570
|
+
/** Count one refusal; write a line if this key is new or its interval passed. */
|
|
84571
|
+
note(refusal) {
|
|
84572
|
+
const key = keyOf(refusal);
|
|
84573
|
+
const at = this.now();
|
|
84574
|
+
const held = this.keys.get(key);
|
|
84575
|
+
const pending = (held?.pending ?? 0) + 1;
|
|
84576
|
+
if (held === void 0 || at - held.lastLineAtMs >= this.intervalMs) {
|
|
84577
|
+
this.write(refusal, pending, held === void 0 ? "first" : "interval");
|
|
84578
|
+
this.keys = new Map(this.keys).set(key, {
|
|
84579
|
+
last: refusal,
|
|
84580
|
+
pending: 0,
|
|
84581
|
+
lastLineAtMs: at
|
|
84582
|
+
});
|
|
84583
|
+
return;
|
|
84584
|
+
}
|
|
84585
|
+
this.keys = new Map(this.keys).set(key, {
|
|
84586
|
+
...held,
|
|
84587
|
+
last: refusal,
|
|
84588
|
+
pending
|
|
84589
|
+
});
|
|
84590
|
+
}
|
|
84591
|
+
/** Report every key whose interval passed with refusals still pending. */
|
|
84592
|
+
sweep() {
|
|
84593
|
+
const at = this.now();
|
|
84594
|
+
for (const [key, held] of this.keys) {
|
|
84595
|
+
if (held.pending === 0 || at - held.lastLineAtMs < this.intervalMs) continue;
|
|
84596
|
+
this.write(held.last, held.pending, "interval");
|
|
84597
|
+
this.keys = new Map(this.keys).set(key, {
|
|
84598
|
+
...held,
|
|
84599
|
+
pending: 0,
|
|
84600
|
+
lastLineAtMs: at
|
|
84601
|
+
});
|
|
84602
|
+
}
|
|
84603
|
+
}
|
|
84604
|
+
/** Report every pending count now, whatever its interval (shutdown). */
|
|
84605
|
+
flush() {
|
|
84606
|
+
for (const held of this.keys.values()) if (held.pending > 0) this.write(held.last, held.pending, "flush");
|
|
84607
|
+
this.keys = /* @__PURE__ */ new Map();
|
|
84608
|
+
}
|
|
84609
|
+
write(r, refused, closing) {
|
|
84610
|
+
this.logger.warn(MESSAGES[r.reason], {
|
|
84611
|
+
tags: { deviceId: r.deviceId },
|
|
84612
|
+
meta: {
|
|
84613
|
+
reason: r.reason,
|
|
84614
|
+
modelId: r.modelId,
|
|
84615
|
+
refused,
|
|
84616
|
+
windowMs: this.intervalMs,
|
|
84617
|
+
closing,
|
|
84618
|
+
lastTrackId: r.trackId,
|
|
84619
|
+
...r.dim !== void 0 ? { dim: r.dim } : {},
|
|
84620
|
+
...r.expected !== void 0 ? { expected: r.expected } : {}
|
|
84621
|
+
}
|
|
84622
|
+
});
|
|
84623
|
+
}
|
|
84624
|
+
};
|
|
84225
84625
|
//#endregion
|
|
84226
84626
|
//#region src/pipeline-analytics/store/object-embedding-store.ts
|
|
84227
84627
|
/**
|
|
@@ -84284,10 +84684,33 @@ var ObjectEmbeddingStore = class {
|
|
|
84284
84684
|
clipModels;
|
|
84285
84685
|
/** index → the dim this process declared it at. Per index, per process. */
|
|
84286
84686
|
declaredDims = /* @__PURE__ */ new Map();
|
|
84687
|
+
/**
|
|
84688
|
+
* Refused writes, COUNTED per (camera, model, reason). A refusal is systemic
|
|
84689
|
+
* — a runner stamping the wrong model refuses every vector it sends — so a
|
|
84690
|
+
* per-track WARN was a flood that hid the one fact that mattered (D658).
|
|
84691
|
+
*/
|
|
84692
|
+
refusals;
|
|
84287
84693
|
constructor(deps) {
|
|
84288
84694
|
this.vectorStore = deps.vectorStore;
|
|
84289
84695
|
this.logger = deps.logger;
|
|
84290
84696
|
this.clipModels = deps.clipModels;
|
|
84697
|
+
this.refusals = new EmbeddingRefusalReport({
|
|
84698
|
+
logger: deps.logger,
|
|
84699
|
+
...deps.refusalReportIntervalMs !== void 0 ? { intervalMs: deps.refusalReportIntervalMs } : {},
|
|
84700
|
+
...deps.now !== void 0 ? { now: deps.now } : {}
|
|
84701
|
+
});
|
|
84702
|
+
}
|
|
84703
|
+
/**
|
|
84704
|
+
* Count a write refused BEFORE it reached this store — a detail whose result
|
|
84705
|
+
* named no model (D658). Same report, same rate limit, as the refusals the
|
|
84706
|
+
* store makes itself.
|
|
84707
|
+
*/
|
|
84708
|
+
noteRefused(refusal) {
|
|
84709
|
+
this.refusals.note(refusal);
|
|
84710
|
+
}
|
|
84711
|
+
/** Report every refusal count still pending (shutdown). */
|
|
84712
|
+
flushRefusals() {
|
|
84713
|
+
this.refusals.flush();
|
|
84291
84714
|
}
|
|
84292
84715
|
/** Every index a delete, scan or wipe must reach. */
|
|
84293
84716
|
indexes() {
|
|
@@ -84385,19 +84808,28 @@ var ObjectEmbeddingStore = class {
|
|
|
84385
84808
|
* and dropped", because a counter that cannot reads as verification.
|
|
84386
84809
|
*/
|
|
84387
84810
|
async upsertIfBetter(input) {
|
|
84811
|
+
this.refusals.sweep();
|
|
84388
84812
|
const vector = resolveVectorPayload(input);
|
|
84389
84813
|
if (vector === null) return "failed";
|
|
84390
|
-
const space = this.
|
|
84391
|
-
if (space === null)
|
|
84814
|
+
const space = this.clipModels.spaceFor(input.modelId);
|
|
84815
|
+
if (space === null) {
|
|
84816
|
+
this.refusals.note({
|
|
84817
|
+
deviceId: input.deviceId,
|
|
84818
|
+
modelId: input.modelId,
|
|
84819
|
+
reason: "no-clip-metadata",
|
|
84820
|
+
trackId: input.trackId,
|
|
84821
|
+
dim: vector.dim
|
|
84822
|
+
});
|
|
84823
|
+
return "failed";
|
|
84824
|
+
}
|
|
84392
84825
|
if (vector.dim !== space.dim) {
|
|
84393
|
-
this.
|
|
84394
|
-
|
|
84395
|
-
|
|
84396
|
-
|
|
84397
|
-
|
|
84398
|
-
|
|
84399
|
-
|
|
84400
|
-
}
|
|
84826
|
+
this.refusals.note({
|
|
84827
|
+
deviceId: input.deviceId,
|
|
84828
|
+
modelId: input.modelId,
|
|
84829
|
+
reason: "dimension-mismatch",
|
|
84830
|
+
trackId: input.trackId,
|
|
84831
|
+
dim: vector.dim,
|
|
84832
|
+
expected: space.dim
|
|
84401
84833
|
});
|
|
84402
84834
|
return "failed";
|
|
84403
84835
|
}
|
|
@@ -84871,6 +85303,30 @@ function rowFromMetadata(id, metadata) {
|
|
|
84871
85303
|
};
|
|
84872
85304
|
}
|
|
84873
85305
|
//#endregion
|
|
85306
|
+
//#region src/pipeline-analytics/clip-detail-route.ts
|
|
85307
|
+
async function routeClipDetailToStore(store, input) {
|
|
85308
|
+
const modelId = input.embeddingModelId;
|
|
85309
|
+
if (modelId === void 0 || modelId === "") {
|
|
85310
|
+
store.noteRefused({
|
|
85311
|
+
deviceId: input.deviceId,
|
|
85312
|
+
modelId: null,
|
|
85313
|
+
reason: "no-model-id",
|
|
85314
|
+
trackId: input.trackId
|
|
85315
|
+
});
|
|
85316
|
+
return "failed";
|
|
85317
|
+
}
|
|
85318
|
+
return store.upsertIfBetter({
|
|
85319
|
+
trackId: input.trackId,
|
|
85320
|
+
deviceId: input.deviceId,
|
|
85321
|
+
timestamp: input.timestamp,
|
|
85322
|
+
className: input.className,
|
|
85323
|
+
embeddingBase64: input.embeddingBase64,
|
|
85324
|
+
modelId,
|
|
85325
|
+
confidence: input.score,
|
|
85326
|
+
...input.keyFrameMediaId !== void 0 ? { keyFrameMediaId: input.keyFrameMediaId } : {}
|
|
85327
|
+
});
|
|
85328
|
+
}
|
|
85329
|
+
//#endregion
|
|
84874
85330
|
//#region src/pipeline-analytics/clip-space-plan.ts
|
|
84875
85331
|
/**
|
|
84876
85332
|
* Which CLIP feature space a search reads and a rebuild fills — decided in one
|
|
@@ -87082,9 +87538,6 @@ var KEYFRAME_CROP_MIN_SUBJECT_IOU = .5;
|
|
|
87082
87538
|
* covers a healthy trail many times over; past it the honest answer is "no
|
|
87083
87539
|
* evidence" and the temporal bound takes over. */
|
|
87084
87540
|
var KEYFRAME_CROP_MAX_SAMPLE_GAP_MS = 1e3;
|
|
87085
|
-
/** How long the active CLIP model id (from the embedding-encoder) is cached
|
|
87086
|
-
* before re-reading. */
|
|
87087
|
-
var CLIP_MODEL_ID_CACHE_TTL_MS = 6e4;
|
|
87088
87541
|
var SETTINGS_CACHE_TTL_MS = 5e3;
|
|
87089
87542
|
/** TTL for the per-node confirmation-gate detector-step cache (the node's
|
|
87090
87543
|
* default object-detector step rarely changes). */
|
|
@@ -87375,10 +87828,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
87375
87828
|
overlayState = new OverlayDetailStateStore();
|
|
87376
87829
|
/** Throttle state for the "overlay synthesis failed" warn, one per device. */
|
|
87377
87830
|
overlaySynthesisWarnAt = /* @__PURE__ */ new Map();
|
|
87378
|
-
/** Active CLIP image-embedding model id (== the text encoder's), TTL-cached.
|
|
87379
|
-
* Stamped on object-embedding rows from the detail plane so semantic search's
|
|
87380
|
-
* same-model gate keeps matching. */
|
|
87381
|
-
clipModelIdCache = null;
|
|
87382
87831
|
/** Frame-based event/track media (crop + boxed full-frame) from the
|
|
87383
87832
|
* detection-pipeline DECODED frame — the ONLY image source (never the
|
|
87384
87833
|
* snapshot cap). Null when shm frame access is unavailable. */
|
|
@@ -91254,6 +91703,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
91254
91703
|
await Promise.all([this.drainSensorHandlers(), this.motionEpisodes?.closeAll("addon-shutdown")]);
|
|
91255
91704
|
await this.embeddingDispatcher?.stop();
|
|
91256
91705
|
this.embeddingDispatcher = null;
|
|
91706
|
+
this.objectEmbeddingStore?.flushRefusals();
|
|
91257
91707
|
await this.notificationCenter?.stop();
|
|
91258
91708
|
this.notificationCenter = null;
|
|
91259
91709
|
for (const id of this.proxies.keys()) this.releaseProxy(id);
|
|
@@ -92936,31 +93386,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
92936
93386
|
});
|
|
92937
93387
|
}
|
|
92938
93388
|
/** CLIP object-embedding detail → the object-embedding store (semantic
|
|
92939
|
-
* search)
|
|
92940
|
-
* gate keeps matching. */
|
|
93389
|
+
* search), under the model the RUNNER stamped — never a guess (D658). */
|
|
92941
93390
|
async routeClipDetail(deviceId, trackId, detail, timestamp) {
|
|
92942
93391
|
const store = this.objectEmbeddingStore;
|
|
92943
93392
|
if (!store || detail.embedding === void 0) return;
|
|
92944
|
-
const modelId = detail.embeddingModelId ?? await this.resolveClipModelId();
|
|
92945
|
-
if (modelId === null) {
|
|
92946
|
-
this.ctx.logger.debug("clip detail dropped — no active embedding model id", {
|
|
92947
|
-
tags: { deviceId },
|
|
92948
|
-
meta: {
|
|
92949
|
-
trackId,
|
|
92950
|
-
stepId: detail.stepId
|
|
92951
|
-
}
|
|
92952
|
-
});
|
|
92953
|
-
return;
|
|
92954
|
-
}
|
|
92955
93393
|
const keyFrameMediaId = this.residents.keyFrameMediaId(trackId);
|
|
92956
|
-
await store
|
|
92957
|
-
trackId,
|
|
93394
|
+
await routeClipDetailToStore(store, {
|
|
92958
93395
|
deviceId,
|
|
93396
|
+
trackId,
|
|
92959
93397
|
timestamp,
|
|
92960
93398
|
className: detail.className,
|
|
92961
|
-
|
|
92962
|
-
|
|
92963
|
-
|
|
93399
|
+
score: detail.score,
|
|
93400
|
+
embeddingBase64: detail.embedding,
|
|
93401
|
+
embeddingModelId: detail.embeddingModelId,
|
|
92964
93402
|
...keyFrameMediaId !== void 0 ? { keyFrameMediaId } : {}
|
|
92965
93403
|
});
|
|
92966
93404
|
}
|
|
@@ -92989,23 +93427,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
92989
93427
|
...identityId !== void 0 ? { identityId } : {}
|
|
92990
93428
|
});
|
|
92991
93429
|
}
|
|
92992
|
-
/** Active CLIP image-embedding model id (== the text encoder's), TTL-cached.
|
|
92993
|
-
* Returns null when the embedding-encoder cap is unavailable. */
|
|
92994
|
-
async resolveClipModelId() {
|
|
92995
|
-
const now = Date.now();
|
|
92996
|
-
if (this.clipModelIdCache && now < this.clipModelIdCache.expiresAt) return this.clipModelIdCache.value;
|
|
92997
|
-
let value = null;
|
|
92998
|
-
try {
|
|
92999
|
-
value = (await this.ctx.api.embeddingEncoder.getInfo.query())?.modelId ?? null;
|
|
93000
|
-
} catch (err) {
|
|
93001
|
-
this.ctx.logger.debug("resolveClipModelId: getInfo failed", { meta: { error: errMsg$1(err) } });
|
|
93002
|
-
}
|
|
93003
|
-
this.clipModelIdCache = {
|
|
93004
|
-
value,
|
|
93005
|
-
expiresAt: now + CLIP_MODEL_ID_CACHE_TTL_MS
|
|
93006
|
-
};
|
|
93007
|
-
return value;
|
|
93008
|
-
}
|
|
93009
93430
|
/**
|
|
93010
93431
|
* §5 — decide which active tracks need periodic media THIS frame. Pure over
|
|
93011
93432
|
* TrackStore.lastSnapshotAt + the per-track best-confidence map:
|
|
@@ -96931,6 +97352,126 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
96931
97352
|
}
|
|
96932
97353
|
}
|
|
96933
97354
|
/**
|
|
97355
|
+
* D658: re-embed EXACTLY the named samples with ONE pinned model, forced
|
|
97356
|
+
* (`face-sample-reembed.ts`) — the repair for what the audit accuses. Shares
|
|
97357
|
+
* the gallery-job exclusion with the re-embed pass, the photo pass and the
|
|
97358
|
+
* reset, and reloads the matcher's gallery when anything landed.
|
|
97359
|
+
*/
|
|
97360
|
+
async reembedFaceGallerySamples(input) {
|
|
97361
|
+
const api = this.ctx.api;
|
|
97362
|
+
if (!api) throw new Error("faceGallery.reembedSamples: the addon API is not available");
|
|
97363
|
+
const identityStore = this.identityStore;
|
|
97364
|
+
if (!identityStore) throw new Error("faceGallery.reembedSamples: the identity store is not ready");
|
|
97365
|
+
const mediaStore = this.mediaStore;
|
|
97366
|
+
if (!mediaStore) throw new Error("faceGallery.reembedSamples: the media store is not ready");
|
|
97367
|
+
if (this.faceReembedRunning) throw new Error("faceGallery.reembedSamples: a face re-embed pass is already running");
|
|
97368
|
+
if (this.faceAlignmentJobs?.isPhotoPassRunning === true) throw new Error("faceGallery.reembedSamples: a photo re-enrol pass is running (D643)");
|
|
97369
|
+
if (this.faceAlignmentJobs?.isResetRunning === true) throw new Error("faceGallery.reembedSamples: a face gallery reset is running (D644)");
|
|
97370
|
+
this.faceReembedRunning = true;
|
|
97371
|
+
try {
|
|
97372
|
+
const embedder = new FaceReembedEmbedder({
|
|
97373
|
+
runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
|
|
97374
|
+
logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
|
|
97375
|
+
}, await this.resolveRebuildNodes(api, input.executeOnNodeId), input.modelId);
|
|
97376
|
+
const result = await reembedFaceSamples({
|
|
97377
|
+
listSamples: () => identityStore.listSamplesForReembed(),
|
|
97378
|
+
loadAlignedCrop: async (sample) => {
|
|
97379
|
+
if (sample.mediaId === void 0) return null;
|
|
97380
|
+
try {
|
|
97381
|
+
const file = await mediaStore.getById(sample.mediaId);
|
|
97382
|
+
return file === null ? null : Buffer.from(file.base64, "base64");
|
|
97383
|
+
} catch (err) {
|
|
97384
|
+
this.ctx.logger.warn("face sample re-embed: aligned crop read threw", { meta: {
|
|
97385
|
+
sampleId: sample.sampleId,
|
|
97386
|
+
error: String(err)
|
|
97387
|
+
} });
|
|
97388
|
+
return null;
|
|
97389
|
+
}
|
|
97390
|
+
},
|
|
97391
|
+
embed: (sample, crop) => embedder.embed(sample, crop),
|
|
97392
|
+
store: (sample, embedding, modelId, dim) => identityStore.restampSample({
|
|
97393
|
+
sampleId: sample.sampleId,
|
|
97394
|
+
identityId: sample.identityId,
|
|
97395
|
+
embedding,
|
|
97396
|
+
modelId,
|
|
97397
|
+
dim
|
|
97398
|
+
}),
|
|
97399
|
+
pause: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
97400
|
+
logger: {
|
|
97401
|
+
info: (m, e) => this.ctx.logger.info(m, e),
|
|
97402
|
+
warn: (m, e) => this.ctx.logger.warn(m, e)
|
|
97403
|
+
}
|
|
97404
|
+
}, {
|
|
97405
|
+
sampleIds: input.sampleIds,
|
|
97406
|
+
modelId: input.modelId
|
|
97407
|
+
});
|
|
97408
|
+
if (result.reembedded > 0) await this.faceRecognizer?.refreshGallery();
|
|
97409
|
+
return {
|
|
97410
|
+
...result,
|
|
97411
|
+
rows: [...result.rows]
|
|
97412
|
+
};
|
|
97413
|
+
} finally {
|
|
97414
|
+
this.faceReembedRunning = false;
|
|
97415
|
+
}
|
|
97416
|
+
}
|
|
97417
|
+
/**
|
|
97418
|
+
* D658: re-embed each sample's stored template with ONE pinned model and
|
|
97419
|
+
* compare with its stored vector (`face-embedding-audit.ts`). READ-ONLY —
|
|
97420
|
+
* nothing is restamped, re-embedded or removed. The pass pins the model the
|
|
97421
|
+
* caller names (default `auraface-r100`), not the cluster row: the question
|
|
97422
|
+
* is whether a STAMP is true, whatever the cluster runs today.
|
|
97423
|
+
*/
|
|
97424
|
+
async auditFaceGalleryEmbeddings(input) {
|
|
97425
|
+
const api = this.ctx.api;
|
|
97426
|
+
if (!api) throw new Error("faceGallery.auditEmbeddings: the addon API is not available");
|
|
97427
|
+
const identityStore = this.identityStore;
|
|
97428
|
+
if (!identityStore) throw new Error("faceGallery.auditEmbeddings: the identity store is not ready");
|
|
97429
|
+
const mediaStore = this.mediaStore;
|
|
97430
|
+
if (!mediaStore) throw new Error("faceGallery.auditEmbeddings: the media store is not ready");
|
|
97431
|
+
const auditedModelId = input.modelId ?? "auraface-r100";
|
|
97432
|
+
const embedder = new FaceReembedEmbedder({
|
|
97433
|
+
runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
|
|
97434
|
+
logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
|
|
97435
|
+
}, await this.resolveRebuildNodes(api, input.executeOnNodeId), auditedModelId);
|
|
97436
|
+
const report = await auditFaceEmbeddings({
|
|
97437
|
+
listSamples: () => identityStore.listSamplesForReembed(),
|
|
97438
|
+
loadStoredVectors: (ids) => identityStore.loadSampleVectors(ids),
|
|
97439
|
+
loadAlignedCrop: async (sample) => {
|
|
97440
|
+
if (sample.mediaId === void 0) return null;
|
|
97441
|
+
try {
|
|
97442
|
+
const file = await mediaStore.getById(sample.mediaId);
|
|
97443
|
+
return file === null ? null : Buffer.from(file.base64, "base64");
|
|
97444
|
+
} catch (err) {
|
|
97445
|
+
this.ctx.logger.warn("face embedding audit: aligned crop read threw", { meta: {
|
|
97446
|
+
sampleId: sample.sampleId,
|
|
97447
|
+
error: String(err)
|
|
97448
|
+
} });
|
|
97449
|
+
return null;
|
|
97450
|
+
}
|
|
97451
|
+
},
|
|
97452
|
+
embed: (sample, crop) => embedder.embed(sample, crop)
|
|
97453
|
+
}, {
|
|
97454
|
+
auditedModelId,
|
|
97455
|
+
...input.identityId !== void 0 ? { identityId: input.identityId } : {},
|
|
97456
|
+
...input.maxSamples !== void 0 ? { maxSamples: input.maxSamples } : {}
|
|
97457
|
+
});
|
|
97458
|
+
this.ctx.logger.info("face embedding audit finished", { meta: {
|
|
97459
|
+
auditedModelId,
|
|
97460
|
+
total: report.total,
|
|
97461
|
+
ok: report.ok,
|
|
97462
|
+
mismatch: report.mismatch,
|
|
97463
|
+
inconclusive: report.inconclusive,
|
|
97464
|
+
unmeasurable: report.unmeasurable,
|
|
97465
|
+
complete: report.complete
|
|
97466
|
+
} });
|
|
97467
|
+
return {
|
|
97468
|
+
...report,
|
|
97469
|
+
okMinCosine: AUDIT_OK_MIN_COSINE,
|
|
97470
|
+
mismatchMaxCosine: AUDIT_MISMATCH_MAX_COSINE,
|
|
97471
|
+
rows: [...report.rows]
|
|
97472
|
+
};
|
|
97473
|
+
}
|
|
97474
|
+
/**
|
|
96934
97475
|
* The server-side half of "change the model from any UI and the gallery
|
|
96935
97476
|
* follows": if a sample sits outside the cluster space and a pass could
|
|
96936
97477
|
* still bring it in, start one. Runs on a timer; converges and goes quiet.
|
|
@@ -97453,6 +97994,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
97453
97994
|
samples: [...result.samples]
|
|
97454
97995
|
};
|
|
97455
97996
|
},
|
|
97997
|
+
"faceGallery.reembedSamples": async (input) => this.reembedFaceGallerySamples(input),
|
|
97998
|
+
"faceGallery.auditEmbeddings": async (input) => this.auditFaceGalleryEmbeddings(input),
|
|
97456
97999
|
"faceGallery.sourceFrameStats": async () => ({ devices: this.faceSourceStats.snapshot().map((d) => ({
|
|
97457
98000
|
...d,
|
|
97458
98001
|
dropped: { ...d.dropped }
|