@camstack/addon-post-analysis 1.2.128 → 1.2.130
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/{dist-CRDS5ykk.mjs → dist-CBCs0-eP.mjs} +2425 -2247
- package/dist/{dist-D6O14xf_.js → dist-CDHL-0gR.js} +2425 -2247
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BZm5TUIO.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CMk3S5nA.mjs} +3 -3
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-UGfPXz4L.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Doc16Tif.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-CQz_Ev1M.mjs → hostInit-B6sMoPF2.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +273 -32
- package/dist/pipeline-analytics/index.mjs +273 -32
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_dist = require("../dist-
|
|
5
|
+
const require_dist = require("../dist-CDHL-0gR.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_dist.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -2446,6 +2446,45 @@ var NcArtifactPlane = class {
|
|
|
2446
2446
|
}
|
|
2447
2447
|
}
|
|
2448
2448
|
/**
|
|
2449
|
+
* Mint a FRESH signed URL for an artefact already sitting on the shelf —
|
|
2450
|
+
* no bytes read, no re-store. The other half of {@link publishIndexed}: that
|
|
2451
|
+
* one stores AND mints once at publish time; this one re-mints on demand for
|
|
2452
|
+
* an id a caller already holds (a history row's `artifactIds`), because the
|
|
2453
|
+
* link `publishIndexed` returned has its own TTL and a caller reaching for
|
|
2454
|
+
* the artefact hours later needs a new one.
|
|
2455
|
+
*
|
|
2456
|
+
* `null` on any of three causes, deliberately indistinguishable to the
|
|
2457
|
+
* caller: no index is wired (nothing to resolve against), the id is unknown
|
|
2458
|
+
* or its artefact has expired past the retained shelf's own age bound, or no
|
|
2459
|
+
* externally-reachable base URL exists for this install. A caller (the Home
|
|
2460
|
+
* Assistant export) must render all three as "no image right now" — never as
|
|
2461
|
+
* a broken link, and never by reusing a URL this call refused to mint.
|
|
2462
|
+
*/
|
|
2463
|
+
async mintUrl(artifactId) {
|
|
2464
|
+
const index = this.deps.index;
|
|
2465
|
+
if (index === void 0) return null;
|
|
2466
|
+
try {
|
|
2467
|
+
const record = await index.resolve(artifactId);
|
|
2468
|
+
if (record === null) return null;
|
|
2469
|
+
if (record.expiresAt <= this.now()) return null;
|
|
2470
|
+
const baseUrl = await this.resolveBaseUrl();
|
|
2471
|
+
if (baseUrl === null) return null;
|
|
2472
|
+
return buildArtifactUrl({
|
|
2473
|
+
baseUrl,
|
|
2474
|
+
routePrefix: this.deps.routePrefix,
|
|
2475
|
+
id: artifactId,
|
|
2476
|
+
secret: this.deps.secret,
|
|
2477
|
+
expMs: this.now() + (this.deps.ttlMs ?? DEFAULT_TTL_MS)
|
|
2478
|
+
});
|
|
2479
|
+
} catch (err) {
|
|
2480
|
+
this.deps.logger.debug("artifact url mint failed", { meta: {
|
|
2481
|
+
artifactId,
|
|
2482
|
+
error: String(err)
|
|
2483
|
+
} });
|
|
2484
|
+
return null;
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
/**
|
|
2449
2488
|
* Absolutize a path against the SAME base URL an artifact link would use.
|
|
2450
2489
|
*
|
|
2451
2490
|
* For links this plane does not host but a RECIPIENT still has to open — a
|
|
@@ -8932,7 +8971,7 @@ var NcDispatcher = class {
|
|
|
8932
8971
|
return { ok: true };
|
|
8933
8972
|
}
|
|
8934
8973
|
}
|
|
8935
|
-
const notification = await this.buildNotification(entry, target);
|
|
8974
|
+
const { notification, artifactIds } = await this.buildNotification(entry, target);
|
|
8936
8975
|
const verdict = await this.runConfirmGate(entry, notification);
|
|
8937
8976
|
if (verdict !== null && verdict.decision === "suppressed") {
|
|
8938
8977
|
this.deps.logger.info("notification suppressed by AI confirm", {
|
|
@@ -8990,10 +9029,11 @@ var NcDispatcher = class {
|
|
|
8990
9029
|
...dropped.length > 0 ? { dropped } : {}
|
|
8991
9030
|
}
|
|
8992
9031
|
});
|
|
8993
|
-
return
|
|
9032
|
+
return {
|
|
8994
9033
|
ok: true,
|
|
8995
|
-
confirm: verdict
|
|
8996
|
-
|
|
9034
|
+
...verdict !== null ? { confirm: verdict } : {},
|
|
9035
|
+
...artifactIds.length > 0 ? { artifactIds } : {}
|
|
9036
|
+
};
|
|
8997
9037
|
} catch (err) {
|
|
8998
9038
|
return {
|
|
8999
9039
|
ok: false,
|
|
@@ -9304,6 +9344,12 @@ var NcDispatcher = class {
|
|
|
9304
9344
|
});
|
|
9305
9345
|
return input.frozen ?? "";
|
|
9306
9346
|
}
|
|
9347
|
+
/**
|
|
9348
|
+
* Builds the notification AND returns the artefact ids its media resolve
|
|
9349
|
+
* indexed — see {@link withArtifactUrls}. A tuple rather than a field on
|
|
9350
|
+
* `NcNotificationInput`: the ids are internal (outbox provenance), never
|
|
9351
|
+
* part of what `notification-output` sends to a backend.
|
|
9352
|
+
*/
|
|
9307
9353
|
async buildNotification(entry, target) {
|
|
9308
9354
|
const subject = entry.payload.subject;
|
|
9309
9355
|
const systemEvent = subject.systemEvent;
|
|
@@ -9332,7 +9378,8 @@ var NcDispatcher = class {
|
|
|
9332
9378
|
vars: { body: rendered },
|
|
9333
9379
|
frozen: rendered
|
|
9334
9380
|
}) : rendered;
|
|
9335
|
-
const
|
|
9381
|
+
const resolvedMedia = await this.withArtifactUrls(entry, await this.resolveAttachments(entry));
|
|
9382
|
+
const attachments = [...resolvedMedia.attachments, ...frozenArtifactAttachments(entry)];
|
|
9336
9383
|
const params = pickParams(entry.payload.params);
|
|
9337
9384
|
const tapUrl = params.clickUrl === void 0 && this.deps.viewerUrlBase !== void 0 ? defaultTapUrl({
|
|
9338
9385
|
deviceId: systemEvent !== void 0 ? 0 : subject.deviceId,
|
|
@@ -9342,21 +9389,24 @@ var NcDispatcher = class {
|
|
|
9342
9389
|
}) : null;
|
|
9343
9390
|
const actions = await this.mintActions(entry, target);
|
|
9344
9391
|
return {
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
|
|
9392
|
+
notification: {
|
|
9393
|
+
body,
|
|
9394
|
+
title,
|
|
9395
|
+
priority: clampPriority(paramNumber(entry.payload.params, "priority") ?? entry.payload.priority),
|
|
9396
|
+
...params,
|
|
9397
|
+
...tapUrl !== null ? { clickUrl: tapUrl } : {},
|
|
9398
|
+
tag: entry.payload.group?.key ?? entry.ruleId,
|
|
9399
|
+
...systemEvent !== void 0 ? systemEvent.deviceId !== void 0 ? { deviceId: systemEvent.deviceId } : {} : { deviceId: subject.deviceId },
|
|
9400
|
+
...subject.eventId !== void 0 ? { eventId: subject.eventId } : {},
|
|
9401
|
+
...systemEvent !== void 0 ? { metadata: {
|
|
9402
|
+
systemEventKind: systemEvent.kind,
|
|
9403
|
+
subject: systemEvent.subject,
|
|
9404
|
+
...systemEvent.data
|
|
9405
|
+
} } : {},
|
|
9406
|
+
...attachments.length > 0 ? { attachments } : {},
|
|
9407
|
+
...actions.length > 0 ? { actions } : {}
|
|
9408
|
+
},
|
|
9409
|
+
artifactIds: resolvedMedia.artifactIds
|
|
9360
9410
|
};
|
|
9361
9411
|
}
|
|
9362
9412
|
/**
|
|
@@ -9400,11 +9450,28 @@ var NcDispatcher = class {
|
|
|
9400
9450
|
}
|
|
9401
9451
|
});
|
|
9402
9452
|
}
|
|
9453
|
+
/**
|
|
9454
|
+
* What one delivery's media resolve produced: the attachments ready to ship
|
|
9455
|
+
* (bytes plus a signed url when the plane could mint one) and the ids the
|
|
9456
|
+
* artefact INDEX assigned them, in the same order — still first, then gif,
|
|
9457
|
+
* then clip. The ids never ride on the attachment objects themselves: those
|
|
9458
|
+
* cross to `notification-output` and a notifier backend has no use for an
|
|
9459
|
+
* internal store id, only for `url`/`bytes`.
|
|
9460
|
+
*
|
|
9461
|
+
* `artifactIds` is what lets a delivery's outbox row later answer "which
|
|
9462
|
+
* artefact was this trigger's picture" (`getHistory.artifactIds`) without a
|
|
9463
|
+
* second write path — the outbox writes back exactly the ids this method
|
|
9464
|
+
* handed to a SUCCESSFUL send, never re-deriving them.
|
|
9465
|
+
*/
|
|
9403
9466
|
async withArtifactUrls(entry, attachments) {
|
|
9404
9467
|
const index = this.deps.indexArtifact;
|
|
9405
9468
|
const publish = this.deps.publishArtifact;
|
|
9406
|
-
if (index === void 0 && publish === void 0) return
|
|
9469
|
+
if (index === void 0 && publish === void 0) return {
|
|
9470
|
+
attachments: [...attachments],
|
|
9471
|
+
artifactIds: []
|
|
9472
|
+
};
|
|
9407
9473
|
const out = [];
|
|
9474
|
+
const artifactIds = [];
|
|
9408
9475
|
let posterId = null;
|
|
9409
9476
|
const deviceId = entry.payload.subject.deviceId;
|
|
9410
9477
|
const common = {
|
|
@@ -9427,6 +9494,7 @@ var NcDispatcher = class {
|
|
|
9427
9494
|
artifactId: null
|
|
9428
9495
|
}));
|
|
9429
9496
|
if (kind === "event-still" && pub.artifactId !== null) posterId = pub.artifactId;
|
|
9497
|
+
if (pub.artifactId !== null) artifactIds.push(pub.artifactId);
|
|
9430
9498
|
out.push(pub.url === null ? { ...att } : {
|
|
9431
9499
|
...att,
|
|
9432
9500
|
url: pub.url
|
|
@@ -9439,7 +9507,10 @@ var NcDispatcher = class {
|
|
|
9439
9507
|
url
|
|
9440
9508
|
});
|
|
9441
9509
|
}
|
|
9442
|
-
return
|
|
9510
|
+
return {
|
|
9511
|
+
attachments: out,
|
|
9512
|
+
artifactIds
|
|
9513
|
+
};
|
|
9443
9514
|
}
|
|
9444
9515
|
/** The full attachment list: the policy still (zone-cropped when the rule
|
|
9445
9516
|
* froze zone ids) plus the footage cut from the broker's clip ring when
|
|
@@ -12543,13 +12614,17 @@ var NcOutbox = class {
|
|
|
12543
12614
|
}
|
|
12544
12615
|
const now = this.now();
|
|
12545
12616
|
if (result.ok) {
|
|
12617
|
+
const payloadPatch = {
|
|
12618
|
+
...result.confirm !== void 0 ? { confirmVerdict: result.confirm } : {},
|
|
12619
|
+
...result.artifactIds !== void 0 && result.artifactIds.length > 0 ? { artifactIds: result.artifactIds } : {}
|
|
12620
|
+
};
|
|
12546
12621
|
const sent = {
|
|
12547
12622
|
...entry,
|
|
12548
12623
|
status: "sent",
|
|
12549
12624
|
updatedAt: now,
|
|
12550
|
-
...
|
|
12625
|
+
...Object.keys(payloadPatch).length > 0 ? { payload: {
|
|
12551
12626
|
...entry.payload,
|
|
12552
|
-
|
|
12627
|
+
...payloadPatch
|
|
12553
12628
|
} } : {}
|
|
12554
12629
|
};
|
|
12555
12630
|
this.pending.delete(entry.id);
|
|
@@ -19583,7 +19658,8 @@ function outboxEntryToHistory(texts, entry) {
|
|
|
19583
19658
|
zones: [...s.zones],
|
|
19584
19659
|
timestamp: s.timestamp,
|
|
19585
19660
|
...s.systemEvent !== void 0 ? { systemEvent: historySystemEvent(texts, s.systemEvent) } : {}
|
|
19586
|
-
}
|
|
19661
|
+
},
|
|
19662
|
+
...entry.payload.artifactIds !== void 0 && entry.payload.artifactIds.length > 0 ? { artifactIds: [...entry.payload.artifactIds] } : {}
|
|
19587
19663
|
};
|
|
19588
19664
|
}
|
|
19589
19665
|
/** The watched question in one grep-able token. */
|
|
@@ -21122,6 +21198,7 @@ var NotificationCenter = class NotificationCenter {
|
|
|
21122
21198
|
}
|
|
21123
21199
|
return { entries: mapped };
|
|
21124
21200
|
},
|
|
21201
|
+
resolveArtifactUrl: async ({ artifactId }) => ({ url: await this.deps.resolveArtifactUrl?.(artifactId) ?? null }),
|
|
21125
21202
|
listDeviceMutes: async () => ({ mutedDeviceIds: this.deviceMutes.listMuted() }),
|
|
21126
21203
|
setDeviceMuted: async ({ deviceId, muted }) => {
|
|
21127
21204
|
await this.deviceMutes.setMuted(deviceId, muted);
|
|
@@ -27217,6 +27294,18 @@ var FaceGalleryProvider = class {
|
|
|
27217
27294
|
* This module names that state (`model-mismatch`) so the UI can say "this face
|
|
27218
27295
|
* is in an old feature space" instead of showing 0% and letting the operator
|
|
27219
27296
|
* conclude the person is not there.
|
|
27297
|
+
*
|
|
27298
|
+
* ## `buildFaceRescoreReport` stays pure — the CALLER persists
|
|
27299
|
+
*
|
|
27300
|
+
* This function never touches the store; every input is passed in and nothing
|
|
27301
|
+
* is read or written (see its own doc below). The operator asked for this
|
|
27302
|
+
* exact recompute, though, and wants the answer to stick — so the ACTION that
|
|
27303
|
+
* wraps this report (`rescoreFaceForTrack` in `pipeline-analytics/index.ts`)
|
|
27304
|
+
* writes the result onto `Face.suggestedIdentityId` / `suggestedMatchScore`,
|
|
27305
|
+
* through `decideRescorePersistence` / `persistRescoreResult` below. That is
|
|
27306
|
+
* still not a background job — it is the SAME explicitly-requested call,
|
|
27307
|
+
* writing once. A `model-mismatch` report (`comparable: false`) is never
|
|
27308
|
+
* written: see `persistRescoreResult`.
|
|
27220
27309
|
*/
|
|
27221
27310
|
/** The empty report every "cannot score" path returns, so shape never varies. */
|
|
27222
27311
|
function emptyReport(verdict, input, comparable) {
|
|
@@ -27292,6 +27381,52 @@ function buildFaceRescoreReport(input) {
|
|
|
27292
27381
|
...best !== void 0 && second !== void 0 ? { runnerUpGap: best.score - second.score } : {}
|
|
27293
27382
|
};
|
|
27294
27383
|
}
|
|
27384
|
+
/**
|
|
27385
|
+
* What `face.rescoreTrack` should PERSIST for a COMPARABLE report, decided as
|
|
27386
|
+
* its own pure function so the rule is directly testable — separated from the
|
|
27387
|
+
* class that owns the store I/O (`rescoreFaceForTrack` in
|
|
27388
|
+
* `pipeline-analytics/index.ts`), which only wires this answer to
|
|
27389
|
+
* `FaceStore.setSuggestion`.
|
|
27390
|
+
*
|
|
27391
|
+
* `null` covers every verdict OTHER than `auto-assignable` / `suggestion`, and
|
|
27392
|
+
* also an `auto-assignable` / `suggestion` verdict whose best-ranked row is
|
|
27393
|
+
* not itself ELIGIBLE (`identities` is ranked by raw score across every
|
|
27394
|
+
* comparable identity, eligible or not — see `buildFaceRescoreReport`). Every
|
|
27395
|
+
* one of those is a genuine "nothing to suggest right now": never an
|
|
27396
|
+
* assignment, and never the winner of an `ambiguous` verdict — the matcher
|
|
27397
|
+
* refused to pick one, so this function does not pick one either.
|
|
27398
|
+
*
|
|
27399
|
+
* Callers MUST gate on `report.comparable` before calling this — a
|
|
27400
|
+
* `model-mismatch` report has no verdict worth acting on; see
|
|
27401
|
+
* {@link persistRescoreResult}, which does that gating and is the one thing
|
|
27402
|
+
* `rescoreFaceForTrack` actually calls.
|
|
27403
|
+
*/
|
|
27404
|
+
function decideRescorePersistence(report) {
|
|
27405
|
+
if (report.verdict !== "auto-assignable" && report.verdict !== "suggestion") return null;
|
|
27406
|
+
const candidate = report.identities.find((row) => row.eligible);
|
|
27407
|
+
return candidate === void 0 ? null : {
|
|
27408
|
+
identityId: candidate.identityId,
|
|
27409
|
+
score: candidate.score
|
|
27410
|
+
};
|
|
27411
|
+
}
|
|
27412
|
+
/**
|
|
27413
|
+
* Persist `face.rescoreTrack`'s result onto the SUGGESTED tier — the ONE
|
|
27414
|
+
* write this explicitly-requested action makes, and the only place that
|
|
27415
|
+
* decides whether it happens at all.
|
|
27416
|
+
*
|
|
27417
|
+
* A non-comparable report (`model-mismatch`) short-circuits BEFORE
|
|
27418
|
+
* `ports.setSuggestion` is ever called: a cosine across model spaces is
|
|
27419
|
+
* well-formed noise, and persisting it would look exactly like a fresh,
|
|
27420
|
+
* meaningful read to every future reader of the row. This is the single most
|
|
27421
|
+
* load-bearing branch in this file — the regression it guards against writes
|
|
27422
|
+
* garbage that reads as data.
|
|
27423
|
+
*/
|
|
27424
|
+
async function persistRescoreResult(faceId, report, ports) {
|
|
27425
|
+
if (!report.comparable) return "not-comparable";
|
|
27426
|
+
const candidate = decideRescorePersistence(report);
|
|
27427
|
+
await ports.setSuggestion(faceId, candidate);
|
|
27428
|
+
return candidate !== null ? "written" : "cleared";
|
|
27429
|
+
}
|
|
27295
27430
|
//#endregion
|
|
27296
27431
|
//#region src/pipeline-analytics/face-rescore-actions.ts
|
|
27297
27432
|
/**
|
|
@@ -27392,8 +27527,10 @@ var FaceRescoreResultSchema = require_dist.object({
|
|
|
27392
27527
|
/** The face's current assignment, so the UI can mark "already this person". */
|
|
27393
27528
|
assigned: require_dist.boolean(),
|
|
27394
27529
|
assignedIdentityId: require_dist.string().optional(),
|
|
27395
|
-
/** What the track has been SHOWING until now —
|
|
27396
|
-
*
|
|
27530
|
+
/** What the track has been SHOWING until now — read BEFORE this call
|
|
27531
|
+
* writes, so it is whatever a previous track close or a previous
|
|
27532
|
+
* `face.rescoreTrack` last persisted. Returned so the UI can put the stale
|
|
27533
|
+
* number beside the fresh one. */
|
|
27397
27534
|
storedSuggestedIdentityId: require_dist.string().optional(),
|
|
27398
27535
|
storedSuggestedMatchScore: require_dist.number().optional(),
|
|
27399
27536
|
verdict: FaceRescoreVerdictSchema,
|
|
@@ -27410,9 +27547,12 @@ var FaceRescoreResultSchema = require_dist.object({
|
|
|
27410
27547
|
});
|
|
27411
27548
|
var faceRescoreActions = require_dist.defineCustomActions({
|
|
27412
27549
|
/**
|
|
27413
|
-
*
|
|
27414
|
-
*
|
|
27415
|
-
*
|
|
27550
|
+
* Persists its own result onto the SUGGESTED tier (`suggestedIdentityId` /
|
|
27551
|
+
* `suggestedMatchScore`) — never an assignment, never automatic; see
|
|
27552
|
+
* `rescoreFaceForTrack` in `pipeline-analytics/index.ts` and
|
|
27553
|
+
* `persistRescoreResult` in `face-rescore.ts`. `admin` because the report
|
|
27554
|
+
* also names every enrolled person and how well an arbitrary face matches
|
|
27555
|
+
* them — the gallery's shape, which is not a viewer-level fact.
|
|
27416
27556
|
*/
|
|
27417
27557
|
"face.rescoreTrack": require_dist.customAction(FaceRescoreInputSchema, FaceRescoreResultSchema, {
|
|
27418
27558
|
kind: "mutation",
|
|
@@ -29407,6 +29547,51 @@ var FrameProcessor = class {
|
|
|
29407
29547
|
}
|
|
29408
29548
|
};
|
|
29409
29549
|
//#endregion
|
|
29550
|
+
//#region src/pipeline-analytics/replay/frame-processor-pass.ts
|
|
29551
|
+
/**
|
|
29552
|
+
* Run a FRESH `FrameProcessor` over a fixed, ordered sequence of frames and
|
|
29553
|
+
* return the tracks it produced.
|
|
29554
|
+
*
|
|
29555
|
+
* No `TrackStore`, no bus, no media — a track here is a summary built
|
|
29556
|
+
* ENTIRELY from `FrameProcessor.process()`'s in-memory return value.
|
|
29557
|
+
*/
|
|
29558
|
+
function runFrameProcessorPass(input) {
|
|
29559
|
+
const processor = new FrameProcessor(input.deviceId, {}, {}, {}, input.source);
|
|
29560
|
+
if (input.zones !== void 0) processor.setZones(input.zones);
|
|
29561
|
+
if (input.detectionRules !== void 0) processor.setDetectionRules(input.detectionRules);
|
|
29562
|
+
if (input.zoneMembershipMinOverlap !== void 0) processor.setZoneMembershipMinOverlap(input.zoneMembershipMinOverlap);
|
|
29563
|
+
const accum = /* @__PURE__ */ new Map();
|
|
29564
|
+
for (const { timestamp, frame } of input.frames) {
|
|
29565
|
+
const result = processor.process({
|
|
29566
|
+
timestamp,
|
|
29567
|
+
frame
|
|
29568
|
+
});
|
|
29569
|
+
for (const td of result.tracked) {
|
|
29570
|
+
const existing = accum.get(td.trackId);
|
|
29571
|
+
const matched = td.matchedThisFrame ? 1 : 0;
|
|
29572
|
+
if (existing === void 0) accum.set(td.trackId, {
|
|
29573
|
+
className: td.className,
|
|
29574
|
+
firstSeenMs: timestamp,
|
|
29575
|
+
lastSeenMs: timestamp,
|
|
29576
|
+
bbox: { ...td.bbox },
|
|
29577
|
+
framesMatched: matched
|
|
29578
|
+
});
|
|
29579
|
+
else {
|
|
29580
|
+
existing.className = td.className;
|
|
29581
|
+
existing.lastSeenMs = timestamp;
|
|
29582
|
+
existing.framesMatched += matched;
|
|
29583
|
+
}
|
|
29584
|
+
}
|
|
29585
|
+
}
|
|
29586
|
+
return { tracks: [...accum.values()].map((t) => ({
|
|
29587
|
+
className: t.className,
|
|
29588
|
+
firstSeenMs: t.firstSeenMs,
|
|
29589
|
+
lastSeenMs: t.lastSeenMs,
|
|
29590
|
+
bbox: t.bbox,
|
|
29591
|
+
framesMatched: t.framesMatched
|
|
29592
|
+
})) };
|
|
29593
|
+
}
|
|
29594
|
+
//#endregion
|
|
29410
29595
|
//#region src/pipeline-analytics/pipeline/semantic-detail-bbox.ts
|
|
29411
29596
|
/** Which box the semantic best-shot should crop: a plate/face DETAIL box
|
|
29412
29597
|
* when we have one, otherwise the subject box. */
|
|
@@ -47629,6 +47814,37 @@ var FaceStore = class {
|
|
|
47629
47814
|
}
|
|
47630
47815
|
}
|
|
47631
47816
|
/**
|
|
47817
|
+
* Persist a `face.rescoreTrack` result onto the SUGGESTED tier — the ONE
|
|
47818
|
+
* write that explicitly-requested action makes. `suggestion: null` means
|
|
47819
|
+
* the recompute found no candidate worth offering and CLEARS both columns
|
|
47820
|
+
* (a genuine "the gallery has nothing for this face right now" result, not
|
|
47821
|
+
* a no-op left at whatever they held before). Never touches `assigned` /
|
|
47822
|
+
* `recognizedIdentityId` — a suggestion is never an assignment. The caller
|
|
47823
|
+
* is responsible for never calling this with a non-comparable (`model-
|
|
47824
|
+
* mismatch`) report; this method persists whatever it is given.
|
|
47825
|
+
*/
|
|
47826
|
+
async setSuggestion(faceId, suggestion) {
|
|
47827
|
+
const data = suggestion === null ? {
|
|
47828
|
+
suggestedIdentityId: null,
|
|
47829
|
+
suggestedMatchScore: null
|
|
47830
|
+
} : {
|
|
47831
|
+
suggestedIdentityId: suggestion.identityId,
|
|
47832
|
+
suggestedMatchScore: suggestion.score
|
|
47833
|
+
};
|
|
47834
|
+
try {
|
|
47835
|
+
await this.store.update.mutate({
|
|
47836
|
+
collection: FACES_COLLECTION,
|
|
47837
|
+
id: faceId,
|
|
47838
|
+
data
|
|
47839
|
+
});
|
|
47840
|
+
} catch (err) {
|
|
47841
|
+
this.logger.warn("FaceStore.setSuggestion failed", { meta: {
|
|
47842
|
+
faceId,
|
|
47843
|
+
error: String(err)
|
|
47844
|
+
} });
|
|
47845
|
+
}
|
|
47846
|
+
}
|
|
47847
|
+
/**
|
|
47632
47848
|
* `TrackScopedStore` contract for the retention cascade: delete the buffered
|
|
47633
47849
|
* face rows of the given tracks that are NOT assigned. Reuses the EXACT
|
|
47634
47850
|
* `assigned` exemption that `prune`/`pruneAll` apply (see the
|
|
@@ -60020,6 +60236,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
60020
60236
|
cancelVision: async (requestId) => {
|
|
60021
60237
|
await api.llm.cancel.mutate({ requestId });
|
|
60022
60238
|
},
|
|
60239
|
+
resolveArtifactUrl: async (artifactId) => await this.ncArtifactPlane?.mintUrl(artifactId) ?? null,
|
|
60023
60240
|
mintActionUrl: (input) => this.ncActionMintUrl?.(input) ?? Promise.resolve(null),
|
|
60024
60241
|
readDeviceStates: async (ids) => {
|
|
60025
60242
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -60538,6 +60755,21 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
60538
60755
|
...input.nodeId !== void 0 ? { nodeId: input.nodeId } : {}
|
|
60539
60756
|
});
|
|
60540
60757
|
}
|
|
60758
|
+
/**
|
|
60759
|
+
* The FrameProcessor pass of a replay run — see the cap doc on
|
|
60760
|
+
* `runReplayFrameProcessor` for why this is not the `processFrame` method
|
|
60761
|
+
* this addon deliberately does not have.
|
|
60762
|
+
*
|
|
60763
|
+
* `runFrameProcessorPass` is pure w.r.t. this addon's own state: it
|
|
60764
|
+
* constructs a FRESH `FrameProcessor` and does not touch `this.trackStore`,
|
|
60765
|
+
* `this.stationaryRegistry` or any other instance field — unlike
|
|
60766
|
+
* `getOrCreateProcessor` (the production path), which wires both.
|
|
60767
|
+
* `scripts/check-replay-isolation.ts` holds that boundary on the
|
|
60768
|
+
* `replay/` directory this delegates into.
|
|
60769
|
+
*/
|
|
60770
|
+
async runReplayFrameProcessor(input) {
|
|
60771
|
+
return runFrameProcessorPass(input);
|
|
60772
|
+
}
|
|
60541
60773
|
async listRetrainAnnotations(input) {
|
|
60542
60774
|
return this.requireRetrain().listAnnotations(input.trackId);
|
|
60543
60775
|
}
|
|
@@ -65487,6 +65719,13 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
65487
65719
|
* See `face-rescore.ts` for why the number already on the track cannot answer
|
|
65488
65720
|
* that: it was computed during live detection and persisted at track close,
|
|
65489
65721
|
* so every enrolment made since is invisible to it.
|
|
65722
|
+
*
|
|
65723
|
+
* The report's result is PERSISTED (`persistRescoreResult`, below) onto
|
|
65724
|
+
* `Face.suggestedIdentityId` / `suggestedMatchScore` — the SAME columns the
|
|
65725
|
+
* live recognizer's suggestion band writes, so a reader never has to know
|
|
65726
|
+
* whether a suggestion came from track close or from this button. Still
|
|
65727
|
+
* never a background job: this write happens once, only because the
|
|
65728
|
+
* operator asked THIS question right now.
|
|
65490
65729
|
*/
|
|
65491
65730
|
async rescoreFaceForTrack(input) {
|
|
65492
65731
|
const api = this.ctx.api;
|
|
@@ -65529,6 +65768,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
65529
65768
|
clusterModelId,
|
|
65530
65769
|
params
|
|
65531
65770
|
});
|
|
65771
|
+
const persisted = await persistRescoreResult(faceId, report, { setSuggestion: (id, suggestion) => faceStore.setSuggestion(id, suggestion) });
|
|
65532
65772
|
this.ctx.logger.info("face rescore", {
|
|
65533
65773
|
tags: { deviceId: input.deviceId },
|
|
65534
65774
|
meta: {
|
|
@@ -65542,7 +65782,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
65542
65782
|
best: report.identities[0]?.name ?? null,
|
|
65543
65783
|
bestScore: report.identities[0]?.score ?? null,
|
|
65544
65784
|
storedSuggestion: face.suggestedMatchScore ?? null,
|
|
65545
|
-
reembedded: reembed.succeeded
|
|
65785
|
+
reembedded: reembed.succeeded,
|
|
65786
|
+
persisted
|
|
65546
65787
|
}
|
|
65547
65788
|
});
|
|
65548
65789
|
return {
|