@camstack/addon-post-analysis 1.2.316 → 1.2.319
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-D-zzCBKK.js → clip-model-registry-BjYpZuuI.js} +9 -5
- package/dist/{clip-model-registry-Dkv8MUCO.mjs → clip-model-registry-DSqobJfx.mjs} +9 -5
- 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-BNvVKYGi.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CKHzDA_B.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bz37odzX.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-Cpm1aEUE.mjs → hostInit-DDDjJDA6.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +406 -30
- package/dist/pipeline-analytics/index.mjs +406 -30
- 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-BAqKeExF.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-BjYpZuuI.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");
|
|
@@ -51362,6 +51362,11 @@ var MEDIA_ID_SEGMENT = "m";
|
|
|
51362
51362
|
/** Path segment that introduces an OWNER question ("best media of"). */
|
|
51363
51363
|
var MEDIA_OWNER_SEGMENT = "o";
|
|
51364
51364
|
/**
|
|
51365
|
+
* Path segment that introduces an event-owner GENERATION (D694) —
|
|
51366
|
+
* `/g/<deviceId>/<recordAt>` after the owner id.
|
|
51367
|
+
*/
|
|
51368
|
+
var MEDIA_GENERATION_SEGMENT = "g";
|
|
51369
|
+
/**
|
|
51365
51370
|
* The plane path for one media row.
|
|
51366
51371
|
*
|
|
51367
51372
|
* `prefix` is `/addon/<addonId>/event-media` — a pure function of the addon
|
|
@@ -51378,6 +51383,23 @@ function mediaFileUrl(prefix, mediaId) {
|
|
|
51378
51383
|
return `${prefix}/${MEDIA_ID_SEGMENT}/${mediaId}`;
|
|
51379
51384
|
}
|
|
51380
51385
|
/**
|
|
51386
|
+
* The plane path for "whatever media best represents this owner".
|
|
51387
|
+
*
|
|
51388
|
+
* The owner id IS encoded: a trackId is a UUID today but an owner id is an
|
|
51389
|
+
* arbitrary row key (the summary mosaic's is `summary-<ruleId>-<windowEndMs>`),
|
|
51390
|
+
* and one slash in it would silently re-cut the path.
|
|
51391
|
+
*
|
|
51392
|
+
* When `generation` is set and `ownerType` is an event table, the path carries
|
|
51393
|
+
* `/g/<deviceId>/<recordAt>` so a reused rowid cannot resolve another record's
|
|
51394
|
+
* crop. Generation is ignored (and not minted) for non-event owners — a track
|
|
51395
|
+
* id is random and needs none.
|
|
51396
|
+
*/
|
|
51397
|
+
function mediaOwnerUrl(prefix, ownerType, ownerId, generation) {
|
|
51398
|
+
const base = `${prefix}/${MEDIA_OWNER_SEGMENT}/${ownerType}/${encodeURIComponent(ownerId)}`;
|
|
51399
|
+
if (generation === void 0 || !require_clip_model_registry.isEventOwnerType(ownerType)) return base;
|
|
51400
|
+
return `${base}/${MEDIA_GENERATION_SEGMENT}/${generation.deviceId}/${generation.recordAt}`;
|
|
51401
|
+
}
|
|
51402
|
+
/**
|
|
51381
51403
|
* A stored row as the wire sees it: the ref, the address, and — transitionally
|
|
51382
51404
|
* — the bytes.
|
|
51383
51405
|
*
|
|
@@ -59230,6 +59252,12 @@ function openGzipStream(out) {
|
|
|
59230
59252
|
onceDrain: (listener) => {
|
|
59231
59253
|
gzip.once("drain", listener);
|
|
59232
59254
|
},
|
|
59255
|
+
flush: () => new Promise((resolve, reject) => {
|
|
59256
|
+
gzip.flush((err) => {
|
|
59257
|
+
if (err !== null && err !== void 0) reject(err);
|
|
59258
|
+
else resolve();
|
|
59259
|
+
});
|
|
59260
|
+
}),
|
|
59233
59261
|
finish: async () => {
|
|
59234
59262
|
gzip.end();
|
|
59235
59263
|
await closed;
|
|
@@ -59325,6 +59353,25 @@ var TapeSegmentWriter = class {
|
|
|
59325
59353
|
});
|
|
59326
59354
|
}
|
|
59327
59355
|
/**
|
|
59356
|
+
* Push the sink's pending gzip output to disk without ending the segment.
|
|
59357
|
+
* A quiet / silent camera otherwise keeps its tail only in the zlib
|
|
59358
|
+
* buffer until the next rotation (D681 follow-up). Never throws into the
|
|
59359
|
+
* caller's frame path: a flush fault is logged and swallowed here.
|
|
59360
|
+
*/
|
|
59361
|
+
async flushToDisk() {
|
|
59362
|
+
try {
|
|
59363
|
+
await this.deps.sink.flush();
|
|
59364
|
+
} catch (err) {
|
|
59365
|
+
this.deps.logger.warn("tracking tape segment flush failed", {
|
|
59366
|
+
tags: { deviceId: this.deps.deviceId },
|
|
59367
|
+
meta: {
|
|
59368
|
+
segmentOriginMs: this.deps.segmentOriginMs,
|
|
59369
|
+
error: require_clip_model_registry.errMsg(err)
|
|
59370
|
+
}
|
|
59371
|
+
});
|
|
59372
|
+
}
|
|
59373
|
+
}
|
|
59374
|
+
/**
|
|
59328
59375
|
* Always ends the segment's records (queue flush, lost-span gap, `end`
|
|
59329
59376
|
* record) before touching the sink, so a sink that cannot finish still
|
|
59330
59377
|
* leaves a decodable body behind it. `sink.finish()`'s rejection is caught
|
|
@@ -59651,6 +59698,21 @@ var TapeRecorder = class {
|
|
|
59651
59698
|
if (cam !== void 0) await this.closeSegment(deviceId, cam, "segment-end");
|
|
59652
59699
|
}
|
|
59653
59700
|
/**
|
|
59701
|
+
* Push every open segment's pending gzip output to disk without rotating
|
|
59702
|
+
* (D681 follow-up). A silent / quiet camera otherwise keeps its tail only
|
|
59703
|
+
* in the zlib buffer until the next frame or the 10-minute cut — a crash
|
|
59704
|
+
* loses it, and `bytesLastHour` lags behind what was already recorded.
|
|
59705
|
+
* Never throws: a per-camera flush fault is logged inside the writer.
|
|
59706
|
+
*/
|
|
59707
|
+
async flushOpenSegments() {
|
|
59708
|
+
const pending = [];
|
|
59709
|
+
for (const cam of this.cameras.values()) {
|
|
59710
|
+
const writer = cam.segment?.writer;
|
|
59711
|
+
if (writer !== void 0 && writer !== null) pending.push(writer.flushToDisk());
|
|
59712
|
+
}
|
|
59713
|
+
await Promise.all(pending);
|
|
59714
|
+
}
|
|
59715
|
+
/**
|
|
59654
59716
|
* Close the camera's open segment only if it holds time at or before
|
|
59655
59717
|
* `atMs` — what a promotion of a window ending at `atMs` needs on disk
|
|
59656
59718
|
* (Task 12 fix round, H1). A window wholly before the open segment leaves
|
|
@@ -60150,6 +60212,29 @@ var TapeShelf = class {
|
|
|
60150
60212
|
};
|
|
60151
60213
|
}
|
|
60152
60214
|
};
|
|
60215
|
+
/**
|
|
60216
|
+
* Log every refusal the sweep already measured (D391 / D681 follow-up).
|
|
60217
|
+
* `sweep()` itself never throws for `rootUnreadable` / per-camera `failed`:
|
|
60218
|
+
* those land on the report, and a caller that only `.catch`es would drop them
|
|
60219
|
+
* silently even when the shelf already warned at the `readdir` site. One
|
|
60220
|
+
* tagged line per refusal at the sweep boundary closes that gap.
|
|
60221
|
+
*/
|
|
60222
|
+
function logTapeSweepReport(report, logger) {
|
|
60223
|
+
if (report.rootUnreadable !== null) logger.warn("tracking tape sweep refused", { meta: {
|
|
60224
|
+
reason: report.rootUnreadable,
|
|
60225
|
+
scope: "root"
|
|
60226
|
+
} });
|
|
60227
|
+
for (const [deviceId, outcome] of report.perDevice) {
|
|
60228
|
+
if (!("failed" in outcome) || outcome.failed !== true) continue;
|
|
60229
|
+
logger.warn("tracking tape sweep refused", {
|
|
60230
|
+
tags: { deviceId },
|
|
60231
|
+
meta: {
|
|
60232
|
+
reason: outcome.reason,
|
|
60233
|
+
scope: "camera"
|
|
60234
|
+
}
|
|
60235
|
+
});
|
|
60236
|
+
}
|
|
60237
|
+
}
|
|
60153
60238
|
//#endregion
|
|
60154
60239
|
//#region src/pipeline-analytics/tracking-bench/recorder/tape-operation-queue.ts
|
|
60155
60240
|
/** Generous: a promotion reads its window's segments and walks back through
|
|
@@ -61203,6 +61288,17 @@ function shelfFigures(listing, now) {
|
|
|
61203
61288
|
...last !== void 0 ? { heldToMs: last.originMs } : {}
|
|
61204
61289
|
};
|
|
61205
61290
|
}
|
|
61291
|
+
/**
|
|
61292
|
+
* Default `debug.tape.status` (no `deviceIds`): drop the noise. An Off camera
|
|
61293
|
+
* with a measured empty shelf is not a cost row and is not promotable; keep
|
|
61294
|
+
* everything else — Recording / Refused / Failing / Unknown, Off with held
|
|
61295
|
+
* segments (still promotable after the switch went off), and Off whose shelf
|
|
61296
|
+
* could not be read (D393: never silently hide an unreadable camera). Explicit
|
|
61297
|
+
* `deviceIds` still return every asked-about camera, Off included (D62).
|
|
61298
|
+
*/
|
|
61299
|
+
function defaultTapeStatusCameras(rows) {
|
|
61300
|
+
return rows.filter((row) => row.state !== "off" || row.segmentsHeld === null || row.segmentsHeld > 0);
|
|
61301
|
+
}
|
|
61206
61302
|
function describeTapeStatus(input) {
|
|
61207
61303
|
return input.deviceIds.map((deviceId) => {
|
|
61208
61304
|
const shelf = {
|
|
@@ -73112,7 +73208,7 @@ var AnalyticsQueryFacade = class {
|
|
|
73112
73208
|
async getMotionEvents(input) {
|
|
73113
73209
|
const events = await (this.deps.eventStore()?.queryMotion(input) ?? Promise.resolve([]));
|
|
73114
73210
|
if (input.projection !== "slim" || this.deps.eventMediaBaseUrl() === null) return events;
|
|
73115
|
-
return this.withMediaUrl(events);
|
|
73211
|
+
return this.withMediaUrl(events, "motion");
|
|
73116
73212
|
}
|
|
73117
73213
|
async getEventDensity(input) {
|
|
73118
73214
|
return this.deps.eventStore()?.densityByKind(input) ?? [];
|
|
@@ -73169,12 +73265,12 @@ var AnalyticsQueryFacade = class {
|
|
|
73169
73265
|
async getObjectEvents(input) {
|
|
73170
73266
|
const events = await (this.deps.eventStore()?.queryObject(input) ?? Promise.resolve([]));
|
|
73171
73267
|
if (input.projection !== "slim" || this.deps.eventMediaBaseUrl() === null) return events;
|
|
73172
|
-
return this.withMediaUrl(events);
|
|
73268
|
+
return this.withMediaUrl(events, "object");
|
|
73173
73269
|
}
|
|
73174
73270
|
async getAudioEvents(input) {
|
|
73175
73271
|
const events = await (this.deps.eventStore()?.queryAudio(input) ?? Promise.resolve([]));
|
|
73176
73272
|
if (input.projection !== "slim" || this.deps.eventMediaBaseUrl() === null) return events;
|
|
73177
|
-
return this.withMediaUrl(events);
|
|
73273
|
+
return this.withMediaUrl(events, "audio");
|
|
73178
73274
|
}
|
|
73179
73275
|
async getEventMedia(input) {
|
|
73180
73276
|
const store = this.deps.mediaStore();
|
|
@@ -73441,13 +73537,20 @@ var AnalyticsQueryFacade = class {
|
|
|
73441
73537
|
*
|
|
73442
73538
|
* Only called when `eventMediaBaseUrl()` is non-null (guard sits in
|
|
73443
73539
|
* the callers), so the template literal is safe.
|
|
73540
|
+
*
|
|
73541
|
+
* The URL names the owner TABLE and the event's GENERATION (D482 + D694):
|
|
73542
|
+
* a bare `${base}/${id}` was both ambiguous across tables and unable to
|
|
73543
|
+
* survive a reused rowid. New stamps always carry `/o/<type>/<id>/g/…`.
|
|
73444
73544
|
*/
|
|
73445
|
-
withMediaUrl(events) {
|
|
73545
|
+
withMediaUrl(events, ownerType) {
|
|
73446
73546
|
const base = this.deps.eventMediaBaseUrl();
|
|
73447
73547
|
if (base === null) return events;
|
|
73448
73548
|
return events.map((e) => ({
|
|
73449
73549
|
...e,
|
|
73450
|
-
mediaUrl:
|
|
73550
|
+
mediaUrl: mediaOwnerUrl(base, ownerType, String(e.id), {
|
|
73551
|
+
deviceId: e.deviceId,
|
|
73552
|
+
recordAt: e.timestamp
|
|
73553
|
+
})
|
|
73451
73554
|
}));
|
|
73452
73555
|
}
|
|
73453
73556
|
};
|
|
@@ -76279,18 +76382,26 @@ function eventMediaEtag(media) {
|
|
|
76279
76382
|
}
|
|
76280
76383
|
/** Digits only — `Number('')` is 0 and `Number(' ')` is 0, so neither is used. */
|
|
76281
76384
|
var DIGITS = /^\d+$/;
|
|
76385
|
+
/** One run of digits, for composing the event-owner remainder pattern. */
|
|
76386
|
+
var DIGIT_RUN = "\\d+";
|
|
76387
|
+
/**
|
|
76388
|
+
* Event-owner remainder with an optional generation suffix (D694):
|
|
76389
|
+
* `<ownerId>` or `<ownerId>/g/<deviceId>/<recordAt>`. Owner ids for event
|
|
76390
|
+
* tables are decimal rowids; the generation segment is the literal `g`.
|
|
76391
|
+
*/
|
|
76392
|
+
var EVENT_OWNER_REMAINDER = new RegExp(`^(${DIGIT_RUN})(?:/${MEDIA_GENERATION_SEGMENT}/(${DIGIT_RUN})/(${DIGIT_RUN}))?$`);
|
|
76282
76393
|
/**
|
|
76283
76394
|
* Read a plane path. `null` for anything that is not one of the two forms —
|
|
76284
76395
|
* including the BARE id the plane used to accept, which is gone: a bare number
|
|
76285
76396
|
* could be a media id or an event id, and after D474 an event id could be one
|
|
76286
76397
|
* of three rows. Ambiguity at the door is what this record removes.
|
|
76287
76398
|
*
|
|
76288
|
-
*
|
|
76289
|
-
* hub's wildcard param is percent-decoded by find-my-way before the
|
|
76290
|
-
* is proxied, so a `%2F` an owner id carried would arrive here as a
|
|
76291
|
-
* slash, and re-cutting on it would silently address a different owner.
|
|
76292
|
-
*
|
|
76293
|
-
*
|
|
76399
|
+
* For non-event owners the id is the whole remainder rather than one segment:
|
|
76400
|
+
* the hub's wildcard param is percent-decoded by find-my-way before the
|
|
76401
|
+
* sub-path is proxied, so a `%2F` an owner id carried would arrive here as a
|
|
76402
|
+
* real slash, and re-cutting on it would silently address a different owner.
|
|
76403
|
+
* Event owners (decimal rowids) may end with `/g/<deviceId>/<recordAt>` —
|
|
76404
|
+
* that suffix is the generation, not part of the id (D694).
|
|
76294
76405
|
*/
|
|
76295
76406
|
function parseMediaAddress(rawPath) {
|
|
76296
76407
|
const path = rawPath.replace(/^\/+/, "");
|
|
@@ -76310,13 +76421,35 @@ function parseMediaAddress(rawPath) {
|
|
|
76310
76421
|
const typeSlash = rest.indexOf("/");
|
|
76311
76422
|
if (typeSlash <= 0) return null;
|
|
76312
76423
|
const ownerType = rest.slice(0, typeSlash);
|
|
76313
|
-
const
|
|
76314
|
-
if (
|
|
76424
|
+
const remainder = rest.slice(typeSlash + 1);
|
|
76425
|
+
if (remainder.length === 0) return null;
|
|
76315
76426
|
if (!require_clip_model_registry.isMediaOwnerType(ownerType)) return null;
|
|
76427
|
+
if (require_clip_model_registry.isEventOwnerType(ownerType)) {
|
|
76428
|
+
const match = EVENT_OWNER_REMAINDER.exec(remainder);
|
|
76429
|
+
if (match === null) return null;
|
|
76430
|
+
const ownerId = match[1];
|
|
76431
|
+
const deviceIdRaw = match[2];
|
|
76432
|
+
const recordAtRaw = match[3];
|
|
76433
|
+
if (ownerId === void 0) return null;
|
|
76434
|
+
if (deviceIdRaw !== void 0 && recordAtRaw !== void 0) return {
|
|
76435
|
+
kind: "owner",
|
|
76436
|
+
ownerType,
|
|
76437
|
+
ownerId,
|
|
76438
|
+
generation: {
|
|
76439
|
+
deviceId: Number(deviceIdRaw),
|
|
76440
|
+
recordAt: Number(recordAtRaw)
|
|
76441
|
+
}
|
|
76442
|
+
};
|
|
76443
|
+
return {
|
|
76444
|
+
kind: "owner",
|
|
76445
|
+
ownerType,
|
|
76446
|
+
ownerId
|
|
76447
|
+
};
|
|
76448
|
+
}
|
|
76316
76449
|
return {
|
|
76317
76450
|
kind: "owner",
|
|
76318
76451
|
ownerType,
|
|
76319
|
-
ownerId
|
|
76452
|
+
ownerId: remainder
|
|
76320
76453
|
};
|
|
76321
76454
|
}
|
|
76322
76455
|
/** Set by the HUB for a `csv_*` share token admitted on this plane; never by a client. */
|
|
@@ -78993,6 +79126,218 @@ function createRetrainExportHandler(deps) {
|
|
|
78993
79126
|
};
|
|
78994
79127
|
}
|
|
78995
79128
|
//#endregion
|
|
79129
|
+
//#region src/pipeline-analytics/retrain/retrain-collection-store.ts
|
|
79130
|
+
/**
|
|
79131
|
+
* @durable class=ledger owner=pipeline-analytics
|
|
79132
|
+
* write="one row per named training collection the operator creates. Names are
|
|
79133
|
+
* unique on nameKey (lower(trim)); empty names never become rows."
|
|
79134
|
+
* retention="none — operator-owned corpus metadata. Rows leave only when an
|
|
79135
|
+
* explicit delete API lands (not V1)."
|
|
79136
|
+
*/
|
|
79137
|
+
var RETRAIN_COLLECTIONS_COLLECTION = "pipeline-analytics:retrain-collections";
|
|
79138
|
+
var RETRAIN_COLLECTION_COLUMNS = [
|
|
79139
|
+
{
|
|
79140
|
+
name: "id",
|
|
79141
|
+
type: "TEXT",
|
|
79142
|
+
primaryKey: true,
|
|
79143
|
+
notNull: true
|
|
79144
|
+
},
|
|
79145
|
+
{
|
|
79146
|
+
name: "name",
|
|
79147
|
+
type: "TEXT",
|
|
79148
|
+
notNull: true
|
|
79149
|
+
},
|
|
79150
|
+
{
|
|
79151
|
+
name: "nameKey",
|
|
79152
|
+
type: "TEXT",
|
|
79153
|
+
notNull: true
|
|
79154
|
+
},
|
|
79155
|
+
{
|
|
79156
|
+
name: "createdAt",
|
|
79157
|
+
type: "INTEGER",
|
|
79158
|
+
notNull: true
|
|
79159
|
+
},
|
|
79160
|
+
{
|
|
79161
|
+
name: "updatedAt",
|
|
79162
|
+
type: "INTEGER",
|
|
79163
|
+
notNull: true
|
|
79164
|
+
}
|
|
79165
|
+
];
|
|
79166
|
+
var RETRAIN_COLLECTION_INDEXES = [{
|
|
79167
|
+
name: "idx_retrain_collections_name_key",
|
|
79168
|
+
columns: ["nameKey"],
|
|
79169
|
+
unique: true
|
|
79170
|
+
}, {
|
|
79171
|
+
name: "idx_retrain_collections_created",
|
|
79172
|
+
columns: ["createdAt"]
|
|
79173
|
+
}];
|
|
79174
|
+
function normalizeCollectionName(raw) {
|
|
79175
|
+
const name = raw.trim();
|
|
79176
|
+
if (name.length === 0) return null;
|
|
79177
|
+
return {
|
|
79178
|
+
name,
|
|
79179
|
+
nameKey: name.toLowerCase()
|
|
79180
|
+
};
|
|
79181
|
+
}
|
|
79182
|
+
var RetrainCollectionStore = class {
|
|
79183
|
+
store;
|
|
79184
|
+
logger;
|
|
79185
|
+
now;
|
|
79186
|
+
newId;
|
|
79187
|
+
countFrames;
|
|
79188
|
+
constructor(deps) {
|
|
79189
|
+
this.store = deps.store;
|
|
79190
|
+
this.logger = deps.logger;
|
|
79191
|
+
this.now = deps.now;
|
|
79192
|
+
this.newId = deps.newId;
|
|
79193
|
+
this.countFrames = deps.countFrames;
|
|
79194
|
+
}
|
|
79195
|
+
static async declare(store) {
|
|
79196
|
+
await store.declareCollection.mutate({
|
|
79197
|
+
collection: RETRAIN_COLLECTIONS_COLLECTION,
|
|
79198
|
+
columns: [...RETRAIN_COLLECTION_COLUMNS],
|
|
79199
|
+
indexes: [...RETRAIN_COLLECTION_INDEXES]
|
|
79200
|
+
});
|
|
79201
|
+
}
|
|
79202
|
+
async create(input) {
|
|
79203
|
+
const normalized = normalizeCollectionName(input.name);
|
|
79204
|
+
if (normalized === null) return {
|
|
79205
|
+
kind: "refused",
|
|
79206
|
+
reason: "empty-name"
|
|
79207
|
+
};
|
|
79208
|
+
if (await this.findByNameKey(normalized.nameKey) !== null) return {
|
|
79209
|
+
kind: "refused",
|
|
79210
|
+
reason: "duplicate-name"
|
|
79211
|
+
};
|
|
79212
|
+
const createdAt = this.now();
|
|
79213
|
+
const id = this.newId();
|
|
79214
|
+
await this.store.set.mutate({
|
|
79215
|
+
collection: RETRAIN_COLLECTIONS_COLLECTION,
|
|
79216
|
+
key: id,
|
|
79217
|
+
value: {
|
|
79218
|
+
name: normalized.name,
|
|
79219
|
+
nameKey: normalized.nameKey,
|
|
79220
|
+
createdAt,
|
|
79221
|
+
updatedAt: createdAt
|
|
79222
|
+
}
|
|
79223
|
+
});
|
|
79224
|
+
return {
|
|
79225
|
+
kind: "created",
|
|
79226
|
+
collection: {
|
|
79227
|
+
id,
|
|
79228
|
+
name: normalized.name,
|
|
79229
|
+
createdAt,
|
|
79230
|
+
frameCount: 0
|
|
79231
|
+
}
|
|
79232
|
+
};
|
|
79233
|
+
}
|
|
79234
|
+
async list() {
|
|
79235
|
+
let rows;
|
|
79236
|
+
try {
|
|
79237
|
+
rows = await this.store.query.query({
|
|
79238
|
+
collection: RETRAIN_COLLECTIONS_COLLECTION,
|
|
79239
|
+
filter: { orderBy: {
|
|
79240
|
+
field: "createdAt",
|
|
79241
|
+
direction: "desc"
|
|
79242
|
+
} }
|
|
79243
|
+
});
|
|
79244
|
+
} catch (err) {
|
|
79245
|
+
this.logger.warn("retrain collections list failed", { meta: { error: String(err) } });
|
|
79246
|
+
return [];
|
|
79247
|
+
}
|
|
79248
|
+
const out = [];
|
|
79249
|
+
for (const row of rows) {
|
|
79250
|
+
const name = typeof row.data["name"] === "string" ? row.data["name"] : "";
|
|
79251
|
+
const createdAt = Number(row.data["createdAt"] ?? 0);
|
|
79252
|
+
const frameCount = this.countFrames !== void 0 ? await this.countFrames(row.id) : 0;
|
|
79253
|
+
out.push({
|
|
79254
|
+
id: row.id,
|
|
79255
|
+
name,
|
|
79256
|
+
createdAt,
|
|
79257
|
+
frameCount
|
|
79258
|
+
});
|
|
79259
|
+
}
|
|
79260
|
+
return out;
|
|
79261
|
+
}
|
|
79262
|
+
async getById(id) {
|
|
79263
|
+
try {
|
|
79264
|
+
const row = await this.store.get.query({
|
|
79265
|
+
collection: RETRAIN_COLLECTIONS_COLLECTION,
|
|
79266
|
+
key: id
|
|
79267
|
+
});
|
|
79268
|
+
if (row === null || row === void 0) return null;
|
|
79269
|
+
const data = row;
|
|
79270
|
+
return {
|
|
79271
|
+
id,
|
|
79272
|
+
name: typeof data["name"] === "string" ? data["name"] : "",
|
|
79273
|
+
createdAt: Number(data["createdAt"] ?? 0),
|
|
79274
|
+
frameCount: this.countFrames !== void 0 ? await this.countFrames(id) : 0
|
|
79275
|
+
};
|
|
79276
|
+
} catch (err) {
|
|
79277
|
+
this.logger.warn("retrain collection get failed", { meta: {
|
|
79278
|
+
collectionId: id,
|
|
79279
|
+
error: String(err)
|
|
79280
|
+
} });
|
|
79281
|
+
return null;
|
|
79282
|
+
}
|
|
79283
|
+
}
|
|
79284
|
+
/**
|
|
79285
|
+
* Stub until still ingest lands. Validates the collection exists, then
|
|
79286
|
+
* refuses with a named reason so callers can wire UI against a stable shape.
|
|
79287
|
+
*/
|
|
79288
|
+
async addRetrainFrameFromRecording(input) {
|
|
79289
|
+
if (await this.getById(input.collectionId) === null) {
|
|
79290
|
+
this.logger.info("retrain collection addFrame refused: unknown-collection", {
|
|
79291
|
+
tags: { deviceId: input.deviceId },
|
|
79292
|
+
meta: {
|
|
79293
|
+
collectionId: input.collectionId,
|
|
79294
|
+
epochMs: input.epochMs
|
|
79295
|
+
}
|
|
79296
|
+
});
|
|
79297
|
+
return {
|
|
79298
|
+
kind: "refused",
|
|
79299
|
+
reason: "unknown-collection"
|
|
79300
|
+
};
|
|
79301
|
+
}
|
|
79302
|
+
this.logger.info("retrain collection addFrame stub: still-capture-unimplemented", {
|
|
79303
|
+
tags: { deviceId: input.deviceId },
|
|
79304
|
+
meta: {
|
|
79305
|
+
collectionId: input.collectionId,
|
|
79306
|
+
epochMs: input.epochMs
|
|
79307
|
+
}
|
|
79308
|
+
});
|
|
79309
|
+
return {
|
|
79310
|
+
kind: "refused",
|
|
79311
|
+
reason: "still-capture-unimplemented",
|
|
79312
|
+
detail: "Still ingest lands in plan Task 3"
|
|
79313
|
+
};
|
|
79314
|
+
}
|
|
79315
|
+
async findByNameKey(nameKey) {
|
|
79316
|
+
try {
|
|
79317
|
+
const row = (await this.store.query.query({
|
|
79318
|
+
collection: RETRAIN_COLLECTIONS_COLLECTION,
|
|
79319
|
+
filter: {
|
|
79320
|
+
where: { nameKey },
|
|
79321
|
+
limit: 1
|
|
79322
|
+
}
|
|
79323
|
+
}))[0];
|
|
79324
|
+
if (row === void 0) return null;
|
|
79325
|
+
return {
|
|
79326
|
+
id: row.id,
|
|
79327
|
+
name: typeof row.data["name"] === "string" ? row.data["name"] : "",
|
|
79328
|
+
createdAt: Number(row.data["createdAt"] ?? 0),
|
|
79329
|
+
frameCount: 0
|
|
79330
|
+
};
|
|
79331
|
+
} catch (err) {
|
|
79332
|
+
this.logger.warn("retrain collection findByNameKey failed", { meta: {
|
|
79333
|
+
nameKey,
|
|
79334
|
+
error: String(err)
|
|
79335
|
+
} });
|
|
79336
|
+
return null;
|
|
79337
|
+
}
|
|
79338
|
+
}
|
|
79339
|
+
};
|
|
79340
|
+
//#endregion
|
|
78996
79341
|
//#region src/pipeline-analytics/retrain/retrain-assist.ts
|
|
78997
79342
|
/** The whole frame, as a normalised rectangle. */
|
|
78998
79343
|
var WHOLE_FRAME = {
|
|
@@ -82402,7 +82747,8 @@ async function rankKeyEvents(candidates, options, peakLookup) {
|
|
|
82402
82747
|
...t.markForTrain !== void 0 ? { markForTrain: t.markForTrain } : {},
|
|
82403
82748
|
...t.retrainStatus !== void 0 ? { retrainStatus: t.retrainStatus } : {},
|
|
82404
82749
|
...t.debug !== void 0 ? { debug: t.debug } : {},
|
|
82405
|
-
...t.favourited !== void 0 ? { favourited: t.favourited } : {}
|
|
82750
|
+
...t.favourited !== void 0 ? { favourited: t.favourited } : {},
|
|
82751
|
+
...t.audioLabels !== void 0 && t.audioLabels.length > 0 ? { audioLabels: t.audioLabels } : {}
|
|
82406
82752
|
});
|
|
82407
82753
|
}
|
|
82408
82754
|
scored.sort((a, b) => b.importance - a.importance);
|
|
@@ -82829,16 +83175,19 @@ var MotionOnsetRegistry = class {
|
|
|
82829
83175
|
* - `resolveSearchThumbnailUrl` — the search-hit thumbnail resolver. It points
|
|
82830
83176
|
* a hit at the CLIP embedding row's OWN crop (the crop captured on the
|
|
82831
83177
|
* embedding's peak frame → the hit thumbnail IS the embedded crop), and only
|
|
82832
|
-
* falls back to the representative event's
|
|
82833
|
-
* crop yet, so a hit never renders blank.
|
|
83178
|
+
* falls back to the representative event's owner ladder when the embedding
|
|
83179
|
+
* row has no crop yet, so a hit never renders blank.
|
|
82834
83180
|
*/
|
|
82835
83181
|
function isClipObjectEmbedding(t, registry = require_clip_model_registry.BUILTIN_CLIP_MODELS) {
|
|
82836
83182
|
return Array.isArray(t.embedding) && t.embedding.length > 0 && typeof t.embeddingModelId === "string" && registry.resolve(t.embeddingModelId) !== null;
|
|
82837
83183
|
}
|
|
82838
83184
|
function resolveSearchThumbnailUrl(input) {
|
|
82839
83185
|
if (input.baseUrl === null) return void 0;
|
|
82840
|
-
|
|
82841
|
-
return
|
|
83186
|
+
if (input.embeddingMediaId !== void 0) return mediaFileUrl(input.baseUrl, input.embeddingMediaId);
|
|
83187
|
+
return mediaOwnerUrl(input.baseUrl, "object", input.eventId, {
|
|
83188
|
+
deviceId: input.deviceId,
|
|
83189
|
+
recordAt: input.recordAt
|
|
83190
|
+
});
|
|
82842
83191
|
}
|
|
82843
83192
|
//#endregion
|
|
82844
83193
|
//#region src/pipeline-analytics/pipeline/onboard-birth-authority.ts
|
|
@@ -100998,7 +101347,10 @@ function ofGeneration(files, scope) {
|
|
|
100998
101347
|
function makeNcOwnerMediaReader(deps) {
|
|
100999
101348
|
return async (ownerKind, ownerId, eventScope) => {
|
|
101000
101349
|
if (ownerKind === "track") return deps.media.listByOwner("track", await deps.resolveTrackOwnerId(ownerId));
|
|
101001
|
-
if (eventScope === void 0)
|
|
101350
|
+
if (eventScope === void 0) {
|
|
101351
|
+
deps.logger.error("object media read refused — NcEventMediaScope required", { meta: { ownerId } });
|
|
101352
|
+
return [];
|
|
101353
|
+
}
|
|
101002
101354
|
const onCamera = await deps.media.listByOwner("object", ownerId, eventScope.deviceId);
|
|
101003
101355
|
const kept = ofGeneration(onCamera, eventScope);
|
|
101004
101356
|
if (kept.length < onCamera.length) deps.logger.warn("event media of another record on a reused event id — not used", {
|
|
@@ -102091,6 +102443,10 @@ var NC_ARTIFACT_PRUNE_INTERVAL_MS = 36e5;
|
|
|
102091
102443
|
* (every camera switched off): the bound is 24 h, not "24 h after the next
|
|
102092
102444
|
* close" (D681). */
|
|
102093
102445
|
var TAPE_SWEEP_INTERVAL_MS = 36e5;
|
|
102446
|
+
/** How often open tape segments push their pending gzip output to disk
|
|
102447
|
+
* without rotating (D681 follow-up): a silent camera otherwise keeps its
|
|
102448
|
+
* tail only in the zlib buffer until the next frame or the 10-minute cut. */
|
|
102449
|
+
var TAPE_FLUSH_INTERVAL_MS = 6e4;
|
|
102094
102450
|
/** What a publish answers on a node whose artifact plane never came up: no
|
|
102095
102451
|
* link, no index row — and, exactly as before, bytes-only delivery. */
|
|
102096
102452
|
var NO_PUBLICATION = {
|
|
@@ -102624,6 +102980,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
102624
102980
|
ncArtifactReadDataPlane = null;
|
|
102625
102981
|
ncArtifactPruneTimer = null;
|
|
102626
102982
|
tapeSweepTimer = null;
|
|
102983
|
+
tapeFlushTimer = null;
|
|
102627
102984
|
/** Why `startTrackingTapes` threw on a node that was allowed to record —
|
|
102628
102985
|
* reported `failing` per switched-on camera, never as `recording`. */
|
|
102629
102986
|
tapeStartFailure = null;
|
|
@@ -104428,6 +104785,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
104428
104785
|
StationaryObjectRegistry.declare(api.settingsStore),
|
|
104429
104786
|
RetrainFrameStore.declare(api.settingsStore),
|
|
104430
104787
|
RetrainAnnotationStore.declare(api.settingsStore),
|
|
104788
|
+
RetrainCollectionStore.declare(api.settingsStore),
|
|
104431
104789
|
OpsLogStore.declare(api.settingsStore),
|
|
104432
104790
|
DebugNoteArchiveStore.declare(api.settingsStore),
|
|
104433
104791
|
TrackingTapeIndexStore.declare(api.settingsStore),
|
|
@@ -106876,6 +107234,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
106876
107234
|
clearInterval(this.tapeSweepTimer);
|
|
106877
107235
|
this.tapeSweepTimer = null;
|
|
106878
107236
|
}
|
|
107237
|
+
if (this.tapeFlushTimer !== null) {
|
|
107238
|
+
clearInterval(this.tapeFlushTimer);
|
|
107239
|
+
this.tapeFlushTimer = null;
|
|
107240
|
+
}
|
|
106879
107241
|
if (this.ncArtifactPruneTimer !== null) {
|
|
106880
107242
|
clearInterval(this.ncArtifactPruneTimer);
|
|
106881
107243
|
this.ncArtifactPruneTimer = null;
|
|
@@ -113649,10 +114011,13 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
113649
114011
|
});
|
|
113650
114012
|
const scored = [];
|
|
113651
114013
|
for (const { event, score } of bestEventByTrackId.values()) {
|
|
114014
|
+
const embeddingMediaId = event.trackId !== void 0 ? embeddingMediaIdByTrackId.get(event.trackId) : void 0;
|
|
113652
114015
|
const mediaUrl = resolveSearchThumbnailUrl({
|
|
113653
114016
|
baseUrl: this.eventMediaBaseUrl,
|
|
113654
114017
|
eventId: String(event.id),
|
|
113655
|
-
|
|
114018
|
+
deviceId: event.deviceId,
|
|
114019
|
+
recordAt: event.timestamp,
|
|
114020
|
+
...embeddingMediaId !== void 0 ? { embeddingMediaId } : {}
|
|
113656
114021
|
});
|
|
113657
114022
|
const keyFrameMediaId = event.trackId !== void 0 ? keyFrameMediaIdByTrackId.get(event.trackId) : void 0;
|
|
113658
114023
|
scored.push({
|
|
@@ -114039,7 +114404,9 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114039
114404
|
logger
|
|
114040
114405
|
});
|
|
114041
114406
|
const sweep = (deviceId) => {
|
|
114042
|
-
shelf.sweep().
|
|
114407
|
+
shelf.sweep().then((report) => {
|
|
114408
|
+
logTapeSweepReport(report, logger);
|
|
114409
|
+
}).catch((err) => {
|
|
114043
114410
|
logger.warn("tracking tape sweep failed", {
|
|
114044
114411
|
...deviceId !== null ? { tags: { deviceId } } : {},
|
|
114045
114412
|
meta: { error: require_clip_model_registry.errMsg(err) }
|
|
@@ -114071,6 +114438,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114071
114438
|
sweep(null);
|
|
114072
114439
|
this.tapeSweepTimer = setInterval(() => sweep(null), TAPE_SWEEP_INTERVAL_MS);
|
|
114073
114440
|
this.tapeSweepTimer.unref?.();
|
|
114441
|
+
this.tapeFlushTimer = setInterval(() => {
|
|
114442
|
+
this.tapeRecorder?.flushOpenSegments();
|
|
114443
|
+
}, TAPE_FLUSH_INTERVAL_MS);
|
|
114444
|
+
this.tapeFlushTimer.unref?.();
|
|
114074
114445
|
this.ctx.logger.info("tracking tapes ready", { meta: { seededProcessors: this.processorInputs.size } });
|
|
114075
114446
|
} catch (err) {
|
|
114076
114447
|
this.tapeStartFailure = require_clip_model_registry.errMsg(err);
|
|
@@ -114263,7 +114634,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114263
114634
|
segments.set(deviceId, await this.tapeListingOf(shelf, deviceId));
|
|
114264
114635
|
}
|
|
114265
114636
|
const recorder = this.tapeRecorder;
|
|
114266
|
-
|
|
114637
|
+
const cameras = describeTapeStatus({
|
|
114267
114638
|
deviceIds,
|
|
114268
114639
|
switchOn,
|
|
114269
114640
|
switchUnreadable,
|
|
@@ -114272,7 +114643,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114272
114643
|
startFailure: this.tapeStartFailure,
|
|
114273
114644
|
segments,
|
|
114274
114645
|
now: Date.now()
|
|
114275
|
-
})
|
|
114646
|
+
});
|
|
114647
|
+
return { cameras: input.deviceIds === void 0 ? defaultTapeStatusCameras(cameras) : cameras };
|
|
114276
114648
|
},
|
|
114277
114649
|
"debug.tape.promote": async (input) => {
|
|
114278
114650
|
const deps = this.tapePromotionDeps();
|
|
@@ -114721,7 +115093,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114721
115093
|
* owner table, so the motion/object coin-flip is gone from here too.
|
|
114722
115094
|
*/
|
|
114723
115095
|
async readMediaByAddress(address, variant, preferKind) {
|
|
114724
|
-
const base = address.kind === "row" ? await this.readMediaById(address.mediaId) : await this.readOwnerThumbnail(address.ownerType, address.ownerId, preferKind);
|
|
115096
|
+
const base = address.kind === "row" ? await this.readMediaById(address.mediaId) : await this.readOwnerThumbnail(address.ownerType, address.ownerId, preferKind, address.generation);
|
|
114725
115097
|
if (base === null || variant === void 0) return base;
|
|
114726
115098
|
return this.applyThumbVariant(base, variant);
|
|
114727
115099
|
}
|
|
@@ -114779,7 +115151,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114779
115151
|
* ~144 MB and allocated ~190 MB of base64 to deliver ~1.2 MB, all of it on
|
|
114780
115152
|
* the single-threaded post-analysis runner.
|
|
114781
115153
|
*/
|
|
114782
|
-
async readOwnerThumbnail(ownerType, rawOwnerId, preferKind) {
|
|
115154
|
+
async readOwnerThumbnail(ownerType, rawOwnerId, preferKind, addressGeneration) {
|
|
114783
115155
|
const ownerId = ownerType === "track" ? await resolveMediaOwnerId(ownerType, rawOwnerId, (id) => this.trackAliasStore?.resolve(id) ?? Promise.resolve(null), (failure) => this.aliasResolveFailureLogger?.(failure), "readOwnerThumbnail") : rawOwnerId;
|
|
114784
115156
|
const listInfo = (ownerKind, id, deviceId) => this.mediaStore?.listInfoByOwner(ownerKind, id, deviceId) ?? Promise.resolve([]);
|
|
114785
115157
|
const getTrackIdForEvent = (eventId) => require_clip_model_registry.isEventOwnerType(ownerType) ? this.eventStore?.getTrackIdForEvent(eventId, ownerType) ?? Promise.resolve(null) : Promise.resolve(null);
|
|
@@ -114793,8 +115165,11 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114793
115165
|
};
|
|
114794
115166
|
};
|
|
114795
115167
|
let ownFiles = await listInfo(ownerType, ownerId);
|
|
114796
|
-
if (require_clip_model_registry.isEventOwnerType(ownerType)
|
|
114797
|
-
const gen =
|
|
115168
|
+
if (require_clip_model_registry.isEventOwnerType(ownerType)) {
|
|
115169
|
+
const gen = addressGeneration !== void 0 ? {
|
|
115170
|
+
deviceId: addressGeneration.deviceId,
|
|
115171
|
+
timestamp: addressGeneration.recordAt
|
|
115172
|
+
} : this.eventStore !== null ? await this.eventStore.eventGenerationOf(ownerType, ownerId) : null;
|
|
114798
115173
|
if (gen !== null) {
|
|
114799
115174
|
const onCamera = await listInfo(ownerType, ownerId, gen.deviceId);
|
|
114800
115175
|
const kept = onCamera.filter((f) => f.timestamp === gen.timestamp);
|
|
@@ -114806,7 +115181,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
114806
115181
|
recordAt: gen.timestamp,
|
|
114807
115182
|
refused: onCamera.length - kept.length,
|
|
114808
115183
|
kept: kept.length,
|
|
114809
|
-
via: "readOwnerThumbnail"
|
|
115184
|
+
via: "readOwnerThumbnail",
|
|
115185
|
+
scopedBy: addressGeneration !== void 0 ? "url" : "live-row"
|
|
114810
115186
|
}
|
|
114811
115187
|
});
|
|
114812
115188
|
ownFiles = kept;
|