@camstack/addon-post-analysis 1.2.363 → 1.2.365
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-B69Mktce.mjs → clip-model-registry-BRXTB6Qa.mjs} +361 -4
- package/dist/{clip-model-registry-D8XeJhT4.js → clip-model-registry-CsJpPkau.js} +390 -3
- 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-CUQTWug8.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DUGlfHVc.mjs} +2 -2
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B3Ko_8Ah.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Cul2786X.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-DOcfQnib.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Duvan2CO.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-Be7_ixng.mjs → hostInit-DH-l3-BR.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +534 -41
- package/dist/pipeline-analytics/index.mjs +534 -41
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as NC_ALARM_DEFAULT_PROFILE_ID, $r as
|
|
1
|
+
import { $ as NC_ALARM_DEFAULT_PROFILE_ID, $r as sceneMonitorCapability, $t as RECORDING_EXPORT_MAX_READ_BYTES, A as EMBEDDING_MODEL_FAILED, Ai as nodePin, An as alarmDeviceSecureOnArm, Ar as mayWriteToLocation, At as NcConditionsSchema, B as LABEL_SUPPLY_MACRO, Bi as number, Bn as buildViewerLink, Br as renameAlarmProfile, Bt as NcSharedSequenceSchema, C as DEFAULT_DETAIL_CROP_CONVENTION, Ci as BaseAddon, Cn as TrackPuritySchema, Cr as isRootSuppliableStepId, Ct as NC_TEMPLATE_VARS, D as DETECTION_PIPELINE_CAP_NAME, Di as defineCustomActions, Dn as TrackSourceSchema, Dr as isVectorIndexDimConflict, Dt as NcAlarmSensorsSchema, E as DETECTION_MACRO_CLASSES, Ei as customAction, En as TrackSideSchema, Er as isSystemEventSummaryRule, Et as NcAlarmSensorsInputSchema, F as EVENT_OWNER_TYPES, Fi as array, Fn as applySecureOnArmEdit, Fr as pipelineAnalyticsCapability, Ft as NcRulePatchSchema, G as MAX_ARCHIVED_DEBUG_NOTES, Gi as string, Gn as cosineSimilarity$1, Gt as NcSystemEventKindSchema, H as ListTrackRelationsInputSchema, Hi as partialRecord, Hn as cloneAlarmSettings, Hr as resolveLocationMode, Ht as NcSnoozeSchema, I as EXPORT_DENSE_MAX_RANGES, Ii as boolean, In as assertTimelapseCadences, Ir as plateGalleryCapability, It as NcRuleSchema, J as MAX_TRACK_PURITIES, Ji as union, Jn as deriveDetailCropRect, Jr as ruleAutoTriggersAlarm, Jt as NcTemplatePreviewSchema, K as MAX_BIRTH_DECISION_RECORDS, Ki as templateLiteral, Kn as createAlarmProfile, Kr as retrainRevisionConflictMessage, Kt as NcTaxonomySchema, L as FIRST_LEVEL_MACRO_CLASSES, Li as discriminatedUnion, Ln as audioMetricsCapability, Lr as readDeviceStateFrom, Lt as NcRuleTargetSchema, M as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Mi as sleep$1, Mn as applyBlockingEdit, Mr as notificationRulesCapability, Mt as NcRuleActionSchema, N as EVENT_DENSITY_BATCH_MAX, Ni as ZodObject, Nn as applyModeDelaysEdits, Nr as occupancyScope, Nt as NcRuleActionsSchema, O as DeclaredDevices, Oi as hydrateSchema, On as activateAlarmProfile, Or as kebabToCamel, Ot as NcAlarmSettingsPatchSchema, P as EVENT_KIND_BY_CAP, Pi as _enum, Pn as applyNonBlockingEdit, Pt as NcRuleInputSchema, Q as MotionTriggeredBySchema, Qn as describeSystemEventFamilyProblem, Qr as ruleKindOf, R as FULL_IMAGE_BBOX, Ri as literal, Rn as audioModeOf, Rr as readMotionTriggeredBy, Rt as NcRuleUpdateInputSchema, S as CoverStateSchema, Si as errMsg, Sn as TimelapseRuleSchema, Sr as isRetiredClearKey, St as NC_TAXONOMY, T as DEFAULT_TIMELAPSE_PREVIEW_TEXT, Ti as createEvent, Tn as TrackRelationSchema, Tr as isSystemDelivery, Tt as NcAlarmPostTriggeredActionSchema, U as ListTrackRelationsResultSchema, Ui as record, Un as consumableLowPercentOf, Ur as resolveModeDelays, Ut as NcSnoozeSuppressedSchema, V as LabelAttributionSchema, Vi as object, Vn as classifyRetrainReview, Vr as resolveClipSearchMinScore, Vt as NcSnoozeInputSchema, W as LockStateSchema, Wi as strictObject, Wn as coreBlockIdFromAddonId, Wr as resolveModeNotifyPolicy, Wt as NcStateSummarySectionIdSchema, X as MOTION_CLOSE_AFTER_MS, Xi as EventCategory, Xn as deriveRecordingMode, Xr as ruleDeviceCompatCapQueries, Xt as OPS_LOG_DEFAULT_LIMIT, Y as MAX_TRACK_RELATIONS, Yi as unknown, Yn as derivePostTriggeredAction, Yr as ruleBelongsToAlarm$1, Yt as NcTemplateVarDescriptorSchema, Z as MediaFileKindEnum, Zn as describeSystemEventDeliveryProblem, Zr as ruleGroupingApplies, Zt as OpsLogEntrySchema, _ as BaseDevice, _i as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, _n as SetTrackRelationInputSchema, _r as isEventKindLabelKey, _t as NC_GROUPING_MIN_SEC, a as CLIP_EMBEDDING_STEP_ID, ai as summarizeReview, an as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, ar as ensureAlarmProfiles, at as NC_ALARM_TRIGGERED_MAX_SEC, b as ClearTrackRelationInputSchema, bi as isSourceCap, bn as TimelapseRuleInputSchema, br as isLabelSupplyStepId, bt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, c as resolveClusterModelPin, ci as systemEventFamilyProblems, cn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, cr as faceGalleryCapability, ct as NC_CLIP_POST_ROLL_DEFAULT_SEC, d as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, di as systemEventSummarySplitOf, dn as SCENE_DEFAULT_UNCOVERED_POLICY, dr as gifFromMp4Args, dt as NC_CONDITION_CATALOG, ei as secureActuationFor, en as RECORDING_SOURCE_CAMSTACK_ADDON, et as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, f as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, fi as templateCarriesSummary, fn as SCENE_DIVERGED, fr as isAlarmControllableCap, ft as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, g as BIRTH_DECISION_DEFAULT_LIMIT, gi as zoneAnalyticsCapability, gn as SetTrackPurityResultSchema, gr as isDetectionMacroClass, gt as NC_GROUPING_MAX_SEC, h as ArchivedDebugNoteSchema, hi as viewerLinkBaseOf, hn as SetTrackPurityInputSchema, hr as isDefaultAlarmProfileId, ht as NC_GROUPING_DEFAULT_SEC, ii as subKindsOf, in as RetrainStatusSchema, ir as encodeVectorBase64, it as NC_ALARM_SYSTEM_EVENT_KINDS, j as EMBEDDING_REQUIREMENTS_FAILED, jn as alarmPanelCapability, jr as memoizePinnedFfmpeg, jt as NcLegacyRuleActionSequenceSchema, k as DetectionSourceSchema, ki as isDeviceScopedCap, kn as addonWidgetsSourceCapability, kr as linkedStepOf, kt as NcConditionDescriptorSchema, l as APPEARANCE_BIN_COUNT, li as systemEventFilterApplies, ln as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, lr as failureContributionCapability, lt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, m as AlarmArmModeSchema, mi as vectorDimFromBase64, mn as SceneMonitorSchema, mr as isAudioClassName, mt as NC_DEFAULT_SNOOZE_MINUTES, ni as servedTemplateVars, nn as RetrainDismissedSchema, nt as NC_ALARM_DELAY_MAX_SEC, o as FACE_EMBEDDING_STEP_ID, oi as syncActiveAlarmProfileSettings, on as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, or as eventKindLabel, ot as NC_AUDIO_CONFIRM_HITS_DEFAULT, p as AUDIO_MACRO_LABELS, pi as templateVarsFor, pn as SCENE_RESET_RECAPTURES, pr as isAppLinkBase, pt as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, q as MAX_BIRTH_LATENCY_PROXY_MS, qi as tuple, qn as deleteAlarmProfile, qr as rootSupplyServes, qt as NcTemplatePreviewInputSchema, ri as storageOccupancyCapability, rn as RetrainOriginSchema, rt as NC_ALARM_NOTIFY_FIELDS, s as readClusterModelPinStrict, si as systemEventDeliveryProblems, sn as SCENE_DEFAULT_ANCHOR_THRESHOLD, sr as evictionPolicyOfLocation, st as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, t as BUILTIN_CLIP_MODELS, ti as sensorRaisedOf, tn as RebuildObjectEmbeddingsRefusalSchema, tr as embeddingRefusalOf, tt as NC_ALARM_DELAY_FIELDS, u as APPEARANCE_BLOCKS, ui as systemEventSummaryScheduleOf, un as SCENE_DEFAULT_QUIET_SECONDS, ur as gifDeriveSpeed, ut as NC_CLIP_RETENTION_MAX_SEC, v as BirthDecisionRecordSchema, vi as canonicalHash, vn as TIMELAPSE_DENSE_FLOOR_SEC, vr as isEventOwnerType, vt as NC_SENSOR_TRANSITION_KINDS, w as DEFAULT_EVENT_COLOR, wi as CamProfileSchema, wn as TrackRelationKindSchema, wr as isScheduleActive, wt as NcAlarmConfigSchema, x as ClearTrackRelationResultSchema, xi as DeviceType, xn as TimelapseRulePatchSchema, xr as isMediaOwnerType, xt as NC_TANK_SYSTEM_EVENT_KINDS, y as COCO_TO_MACRO, yi as evaluateSensorEdge, yn as TRACK_RELATIONS_DEFAULT_LIMIT, yr as isKnownTemplateVar, yt as NC_SNOOZE_MAX_MINUTES, z as FailureCounters, zi as looseObject, zn as buildEventKindDescriptor, zr as readTimelapseGeneratedAt, zt as NcScheduleSchema } from "../clip-model-registry-BRXTB6Qa.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs$1 from "node:fs";
|
|
4
4
|
import { createWriteStream, promises } from "node:fs";
|
|
@@ -48379,6 +48379,7 @@ function startAlarmPanelConvergence(ports) {
|
|
|
48379
48379
|
var NC_ARTIFACT_READ_PREFIX_REF = NC_ARTIFACT_READ_PREFIX;
|
|
48380
48380
|
var TRAINING_EXPORT_PREFIX_REF = "training-export";
|
|
48381
48381
|
var RETRAIN_EXPORT_PREFIX_REF = "retrain-export";
|
|
48382
|
+
var RETRAIN_FRAME_PREFIX_REF = "retrain-frame";
|
|
48382
48383
|
var DEBUG_EXPORT_PREFIX_REF = "debug-export";
|
|
48383
48384
|
var TAPE_EXPORT_PREFIX_REF = "tape-export";
|
|
48384
48385
|
/**
|
|
@@ -48404,6 +48405,13 @@ var PIPELINE_ANALYTICS_PLANE_ACCESS = {
|
|
|
48404
48405
|
/** Every frame behind a marked selection — a corpus, not an item. */
|
|
48405
48406
|
[TRAINING_EXPORT_PREFIX_REF]: "admin",
|
|
48406
48407
|
[RETRAIN_EXPORT_PREFIX_REF]: "admin",
|
|
48408
|
+
/**
|
|
48409
|
+
* One copied retrain frame by its id. `admin`, not `authenticated`: the item
|
|
48410
|
+
* is single, but the cap that served the same bytes (`getRetrainFrameImage`)
|
|
48411
|
+
* is `auth: 'admin'` and the surface is admin curation — widening the bytes
|
|
48412
|
+
* beyond the method they replace would be a policy change nobody asked for.
|
|
48413
|
+
*/
|
|
48414
|
+
[RETRAIN_FRAME_PREFIX_REF]: "admin",
|
|
48407
48415
|
/** Diagnostic bundle: logs, config and frames. */
|
|
48408
48416
|
[DEBUG_EXPORT_PREFIX_REF]: "admin",
|
|
48409
48417
|
/** A promoted tracking tape and its ground truth (D681) — a corpus, not an item. */
|
|
@@ -70911,6 +70919,67 @@ var TrackStore = class {
|
|
|
70911
70919
|
}
|
|
70912
70920
|
};
|
|
70913
70921
|
//#endregion
|
|
70922
|
+
//#region src/pipeline-analytics/retrain/retrain-origin.ts
|
|
70923
|
+
/**
|
|
70924
|
+
* Reading the two JSON columns the review rests on — an annotation's `origin`
|
|
70925
|
+
* and a frame's `dismissed` set — back into typed values.
|
|
70926
|
+
*
|
|
70927
|
+
* Parsed with the cap's own schemas rather than cast: a column written by
|
|
70928
|
+
* something that bypassed the guard reads as "no origin" / "nothing dismissed",
|
|
70929
|
+
* which is the safe direction (a missing snapshot makes a verdict approximate;
|
|
70930
|
+
* it never invents one).
|
|
70931
|
+
*/
|
|
70932
|
+
function parseJsonIfText(value) {
|
|
70933
|
+
if (typeof value !== "string") return value;
|
|
70934
|
+
try {
|
|
70935
|
+
return JSON.parse(value);
|
|
70936
|
+
} catch {
|
|
70937
|
+
return null;
|
|
70938
|
+
}
|
|
70939
|
+
}
|
|
70940
|
+
/** The stored origin snapshot, or `null` (legacy row / unreadable value). */
|
|
70941
|
+
function narrowOrigin(value) {
|
|
70942
|
+
const parsed = RetrainOriginSchema.safeParse(parseJsonIfText(value));
|
|
70943
|
+
return parsed.success ? parsed.data : null;
|
|
70944
|
+
}
|
|
70945
|
+
/** The stored dismissed set, or an empty list. */
|
|
70946
|
+
function narrowDismissed(value) {
|
|
70947
|
+
const parsed = RetrainDismissedSchema.array().safeParse(parseJsonIfText(value));
|
|
70948
|
+
return parsed.success ? parsed.data : [];
|
|
70949
|
+
}
|
|
70950
|
+
/**
|
|
70951
|
+
* The stable name of a pipeline box inside its frame. One live track draws one
|
|
70952
|
+
* box on a frame, and the track id survives a re-derivation, which is what lets
|
|
70953
|
+
* a dismissal made today still match tomorrow's list.
|
|
70954
|
+
*/
|
|
70955
|
+
function pipelineOriginRef(trackId) {
|
|
70956
|
+
return `pipeline:${trackId}`;
|
|
70957
|
+
}
|
|
70958
|
+
/** A re-derived pipeline box as the snapshot an annotation or dismissal keeps. */
|
|
70959
|
+
function pipelineBoxOrigin(box) {
|
|
70960
|
+
return {
|
|
70961
|
+
source: "pipeline",
|
|
70962
|
+
ref: pipelineOriginRef(box.trackId),
|
|
70963
|
+
bbox: box.bbox,
|
|
70964
|
+
macroClass: box.macroClass,
|
|
70965
|
+
...box.label !== void 0 ? { label: box.label } : {},
|
|
70966
|
+
...box.score !== void 0 ? { score: box.score } : {},
|
|
70967
|
+
...box.modelId !== void 0 ? { modelId: box.modelId } : {}
|
|
70968
|
+
};
|
|
70969
|
+
}
|
|
70970
|
+
/** The n-th proposal of a detector (`assist`) or vision-LLM (`llm`) run. */
|
|
70971
|
+
function proposalOrigin(source, index, facts) {
|
|
70972
|
+
return {
|
|
70973
|
+
source,
|
|
70974
|
+
ref: `${source}:${String(index)}`,
|
|
70975
|
+
bbox: facts.bbox,
|
|
70976
|
+
macroClass: facts.macroClass,
|
|
70977
|
+
...facts.label !== void 0 ? { label: facts.label } : {},
|
|
70978
|
+
...facts.score !== void 0 ? { score: facts.score } : {},
|
|
70979
|
+
...facts.modelId !== void 0 ? { modelId: facts.modelId } : {}
|
|
70980
|
+
};
|
|
70981
|
+
}
|
|
70982
|
+
//#endregion
|
|
70914
70983
|
//#region src/pipeline-analytics/retrain/retrain-frame-store.ts
|
|
70915
70984
|
/**
|
|
70916
70985
|
* RetrainFrameStore — the COPIES the retrain dataset owns.
|
|
@@ -71080,6 +71149,22 @@ var RETRAIN_FRAME_COLUMNS = [
|
|
|
71080
71149
|
{
|
|
71081
71150
|
name: "epochMs",
|
|
71082
71151
|
type: "INTEGER"
|
|
71152
|
+
}),
|
|
71153
|
+
(
|
|
71154
|
+
/** Bumped by every annotation save; the compare-and-set a save is guarded
|
|
71155
|
+
* by. The column default makes a pre-revision row read `0`. */
|
|
71156
|
+
{
|
|
71157
|
+
name: "revision",
|
|
71158
|
+
type: "INTEGER",
|
|
71159
|
+
notNull: true,
|
|
71160
|
+
defaultValue: 0
|
|
71161
|
+
}),
|
|
71162
|
+
(
|
|
71163
|
+
/** The proposals the operator deleted (`RetrainDismissed[]`), so a deleted
|
|
71164
|
+
* pipeline box is not offered again. NULL = none. */
|
|
71165
|
+
{
|
|
71166
|
+
name: "dismissed",
|
|
71167
|
+
type: "JSON"
|
|
71083
71168
|
})
|
|
71084
71169
|
];
|
|
71085
71170
|
var RETRAIN_FRAME_INDEXES = [
|
|
@@ -71193,7 +71278,9 @@ var RetrainFrameStore = class {
|
|
|
71193
71278
|
...finiteNumber(data["capturedAt"]) !== void 0 ? { capturedAt: finiteNumber(data["capturedAt"]) } : {},
|
|
71194
71279
|
...finiteNumber(data["chromaSpread"]) !== void 0 ? { chromaSpread: finiteNumber(data["chromaSpread"]) } : {},
|
|
71195
71280
|
complete: data["complete"] === true || data["complete"] === 1,
|
|
71196
|
-
...finiteNumber(data["epochMs"]) !== void 0 ? { epochMs: finiteNumber(data["epochMs"]) } : {}
|
|
71281
|
+
...finiteNumber(data["epochMs"]) !== void 0 ? { epochMs: finiteNumber(data["epochMs"]) } : {},
|
|
71282
|
+
revision: finiteNumber(data["revision"]) ?? 0,
|
|
71283
|
+
dismissed: narrowDismissed(data["dismissed"])
|
|
71197
71284
|
};
|
|
71198
71285
|
}
|
|
71199
71286
|
/** The copies with these ids, in the order given; unknown ids are skipped. */
|
|
@@ -80715,7 +80802,7 @@ async function ensureTrackHierarchyMedia(deps, input) {
|
|
|
80715
80802
|
}
|
|
80716
80803
|
//#endregion
|
|
80717
80804
|
//#region src/pipeline-analytics/event-media-handler.ts
|
|
80718
|
-
var CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
80805
|
+
var CACHE_CONTROL$1 = "public, max-age=31536000, immutable";
|
|
80719
80806
|
/** Default / clamp bounds for the `thumb` variant edge (px). Mirrors
|
|
80720
80807
|
* `shared/frame/square-thumb.ts`; kept here so query parsing stays pure. */
|
|
80721
80808
|
var THUMB_DEFAULT_SIZE = 160;
|
|
@@ -80900,14 +80987,14 @@ function createEventMediaHandler(deps) {
|
|
|
80900
80987
|
if (req.headers["if-none-match"] === etag) {
|
|
80901
80988
|
res.writeHead(304, {
|
|
80902
80989
|
etag,
|
|
80903
|
-
"cache-control": CACHE_CONTROL,
|
|
80990
|
+
"cache-control": CACHE_CONTROL$1,
|
|
80904
80991
|
"server-timing": serverTiming
|
|
80905
80992
|
}).end();
|
|
80906
80993
|
return;
|
|
80907
80994
|
}
|
|
80908
80995
|
res.writeHead(200, {
|
|
80909
80996
|
"content-type": "image/jpeg",
|
|
80910
|
-
"cache-control": CACHE_CONTROL,
|
|
80997
|
+
"cache-control": CACHE_CONTROL$1,
|
|
80911
80998
|
etag,
|
|
80912
80999
|
"content-length": String(media.bytes.byteLength),
|
|
80913
81000
|
"server-timing": serverTiming
|
|
@@ -82837,6 +82924,13 @@ var RETRAIN_ANNOTATION_COLUMNS = [
|
|
|
82837
82924
|
name: "assistScore",
|
|
82838
82925
|
type: "REAL"
|
|
82839
82926
|
},
|
|
82927
|
+
(
|
|
82928
|
+
/** The proposal the box started from (`RetrainOrigin`), snapshotted at
|
|
82929
|
+
* accept time. NULL on a row saved before origins existed. */
|
|
82930
|
+
{
|
|
82931
|
+
name: "origin",
|
|
82932
|
+
type: "JSON"
|
|
82933
|
+
}),
|
|
82840
82934
|
{
|
|
82841
82935
|
name: "exportedInBatch",
|
|
82842
82936
|
type: "TEXT"
|
|
@@ -82920,6 +83014,7 @@ var RetrainAnnotationStore = class {
|
|
|
82920
83014
|
source: narrowSource(data["source"]),
|
|
82921
83015
|
assistModelId: optionalString(data["assistModelId"]),
|
|
82922
83016
|
assistScore: typeof score === "number" ? score : void 0,
|
|
83017
|
+
origin: narrowOrigin(data["origin"]),
|
|
82923
83018
|
exportedInBatch: optionalString(data["exportedInBatch"]),
|
|
82924
83019
|
createdAt: Number(data["createdAt"] ?? 0)
|
|
82925
83020
|
};
|
|
@@ -82937,12 +83032,14 @@ var RetrainAnnotationStore = class {
|
|
|
82937
83032
|
source: a.source,
|
|
82938
83033
|
assistModelId: a.assistModelId ?? null,
|
|
82939
83034
|
assistScore: a.assistScore ?? null,
|
|
83035
|
+
origin: a.origin ?? null,
|
|
82940
83036
|
exportedInBatch: a.exportedInBatch ?? null,
|
|
82941
83037
|
createdAt: a.createdAt
|
|
82942
83038
|
};
|
|
82943
83039
|
}
|
|
82944
83040
|
/**
|
|
82945
|
-
* Replace EVERY annotation on one frame with the supplied set
|
|
83041
|
+
* Replace EVERY annotation on one frame with the supplied set — atomically,
|
|
83042
|
+
* and only if the frame is still at the revision the caller saw.
|
|
82946
83043
|
*
|
|
82947
83044
|
* Whole-frame replacement rather than per-box upsert, deliberately: the unit
|
|
82948
83045
|
* of ground truth is the frame, and "the operator deleted a box" and "the
|
|
@@ -82950,38 +83047,113 @@ var RetrainAnnotationStore = class {
|
|
|
82950
83047
|
* patch API would let a frame keep a box the operator removed on a surface
|
|
82951
83048
|
* that only knew about the boxes it sent.
|
|
82952
83049
|
*
|
|
83050
|
+
* The old rows' deletion, the new rows, the frame's `dismissed` set and its
|
|
83051
|
+
* bumped `revision` are ONE `settingsStore.transact`, guarded by
|
|
83052
|
+
* `commit.fromRevision`: a crash leaves the frame as it was, a concurrent
|
|
83053
|
+
* save leaves one winner and one `conflict` — never a blend, never a frame
|
|
83054
|
+
* blanked between a delete and an insert.
|
|
83055
|
+
*
|
|
82953
83056
|
* Returns the persisted rows so the caller renders ids it can address.
|
|
82954
83057
|
*/
|
|
82955
|
-
async replaceForFrame(mediaKey, drafts) {
|
|
82956
|
-
const existing = await this.listByFrame(mediaKey);
|
|
83058
|
+
async replaceForFrame(mediaKey, drafts, commit) {
|
|
82957
83059
|
const createdAt = this.now();
|
|
82958
83060
|
const rows = drafts.map((d) => ({
|
|
82959
83061
|
...d,
|
|
83062
|
+
origin: d.origin ?? null,
|
|
82960
83063
|
id: this.newId(),
|
|
82961
83064
|
createdAt,
|
|
82962
83065
|
mediaKey
|
|
82963
83066
|
}));
|
|
82964
|
-
|
|
82965
|
-
|
|
82966
|
-
|
|
82967
|
-
|
|
83067
|
+
const nextRevision = commit.fromRevision + 1;
|
|
83068
|
+
const result = await this.store.transact.mutate({
|
|
83069
|
+
guard: {
|
|
83070
|
+
collection: RETRAIN_FRAMES_COLLECTION,
|
|
83071
|
+
key: mediaKey,
|
|
83072
|
+
column: "revision",
|
|
83073
|
+
equals: commit.fromRevision
|
|
83074
|
+
},
|
|
83075
|
+
ops: [
|
|
83076
|
+
{
|
|
83077
|
+
op: "deleteWhere",
|
|
83078
|
+
collection: RETRAIN_ANNOTATIONS_COLLECTION,
|
|
83079
|
+
filter: { where: { mediaKey } }
|
|
83080
|
+
},
|
|
83081
|
+
{
|
|
83082
|
+
op: "insertMany",
|
|
83083
|
+
collection: RETRAIN_ANNOTATIONS_COLLECTION,
|
|
83084
|
+
records: rows.map((row) => ({
|
|
83085
|
+
id: row.id,
|
|
83086
|
+
data: this.toRecord(row)
|
|
83087
|
+
}))
|
|
83088
|
+
},
|
|
83089
|
+
{
|
|
83090
|
+
op: "update",
|
|
83091
|
+
collection: RETRAIN_FRAMES_COLLECTION,
|
|
83092
|
+
id: mediaKey,
|
|
83093
|
+
data: {
|
|
83094
|
+
revision: nextRevision,
|
|
83095
|
+
...commit.dismissed !== void 0 ? { dismissed: [...commit.dismissed] } : {}
|
|
83096
|
+
}
|
|
83097
|
+
}
|
|
83098
|
+
]
|
|
82968
83099
|
});
|
|
82969
|
-
|
|
82970
|
-
|
|
83100
|
+
if (!result.applied) {
|
|
83101
|
+
const current = result.current;
|
|
83102
|
+
return {
|
|
83103
|
+
kind: "conflict",
|
|
83104
|
+
currentRevision: typeof current === "number" ? current : null
|
|
83105
|
+
};
|
|
83106
|
+
}
|
|
83107
|
+
return {
|
|
83108
|
+
kind: "saved",
|
|
83109
|
+
rows,
|
|
83110
|
+
revision: nextRevision
|
|
83111
|
+
};
|
|
83112
|
+
}
|
|
83113
|
+
/**
|
|
83114
|
+
* Set `origin` on rows that have none, in one guarded transaction with a
|
|
83115
|
+
* revision bump. Rows that already carry an origin, or are not on this frame,
|
|
83116
|
+
* are left untouched and not counted. No eligible row ⇒ no write at all.
|
|
83117
|
+
*/
|
|
83118
|
+
async setOrigins(mediaKey, origins, commit) {
|
|
83119
|
+
const eligible = (await this.listByFrame(mediaKey)).filter((row) => !row.origin && origins.has(row.id));
|
|
83120
|
+
if (eligible.length === 0) return {
|
|
83121
|
+
kind: "updated",
|
|
83122
|
+
updated: 0,
|
|
83123
|
+
revision: commit.fromRevision
|
|
83124
|
+
};
|
|
83125
|
+
const nextRevision = commit.fromRevision + 1;
|
|
83126
|
+
const result = await this.store.transact.mutate({
|
|
83127
|
+
guard: {
|
|
83128
|
+
collection: RETRAIN_FRAMES_COLLECTION,
|
|
83129
|
+
key: mediaKey,
|
|
83130
|
+
column: "revision",
|
|
83131
|
+
equals: commit.fromRevision
|
|
83132
|
+
},
|
|
83133
|
+
ops: [...eligible.map((row) => ({
|
|
83134
|
+
op: "update",
|
|
82971
83135
|
collection: RETRAIN_ANNOTATIONS_COLLECTION,
|
|
82972
|
-
|
|
82973
|
-
|
|
82974
|
-
|
|
82975
|
-
|
|
82976
|
-
|
|
82977
|
-
|
|
82978
|
-
|
|
82979
|
-
|
|
82980
|
-
|
|
82981
|
-
|
|
82982
|
-
|
|
83136
|
+
id: row.id,
|
|
83137
|
+
data: { origin: origins.get(row.id) ?? null }
|
|
83138
|
+
})), {
|
|
83139
|
+
op: "update",
|
|
83140
|
+
collection: RETRAIN_FRAMES_COLLECTION,
|
|
83141
|
+
id: mediaKey,
|
|
83142
|
+
data: { revision: nextRevision }
|
|
83143
|
+
}]
|
|
83144
|
+
});
|
|
83145
|
+
if (!result.applied) {
|
|
83146
|
+
const current = result.current;
|
|
83147
|
+
return {
|
|
83148
|
+
kind: "conflict",
|
|
83149
|
+
currentRevision: typeof current === "number" ? current : null
|
|
83150
|
+
};
|
|
82983
83151
|
}
|
|
82984
|
-
return
|
|
83152
|
+
return {
|
|
83153
|
+
kind: "updated",
|
|
83154
|
+
updated: eligible.length,
|
|
83155
|
+
revision: nextRevision
|
|
83156
|
+
};
|
|
82985
83157
|
}
|
|
82986
83158
|
async listByFrame(mediaKey) {
|
|
82987
83159
|
return this.queryWhere({ mediaKey }, `frame ${mediaKey}`);
|
|
@@ -84101,7 +84273,14 @@ var RetrainLlmAssistService = class {
|
|
|
84101
84273
|
profileId: profile.id,
|
|
84102
84274
|
model: answer.model,
|
|
84103
84275
|
leftHouse: leavesHouse,
|
|
84104
|
-
proposals: parsed.objects.map((o) => ({
|
|
84276
|
+
proposals: parsed.objects.map((o, index) => ({
|
|
84277
|
+
origin: proposalOrigin("llm", index, {
|
|
84278
|
+
bbox: o.bbox,
|
|
84279
|
+
macroClass: o.macroClass,
|
|
84280
|
+
...o.label !== void 0 ? { label: o.label } : {},
|
|
84281
|
+
score: o.confidence,
|
|
84282
|
+
modelId: answer.model
|
|
84283
|
+
}),
|
|
84105
84284
|
bbox: o.bbox,
|
|
84106
84285
|
macroClass: o.macroClass,
|
|
84107
84286
|
...o.label !== void 0 ? { label: o.label } : {},
|
|
@@ -84446,6 +84625,87 @@ async function serveRetrainArchive(_req, res, writeArchive) {
|
|
|
84446
84625
|
}
|
|
84447
84626
|
}
|
|
84448
84627
|
//#endregion
|
|
84628
|
+
//#region src/pipeline-analytics/retrain/retrain-frame-handler.ts
|
|
84629
|
+
var RETRAIN_FRAME_PREFIX = "retrain-frame";
|
|
84630
|
+
/** Longest side of a `size=thumb` response. */
|
|
84631
|
+
var RETRAIN_THUMB_MAX_SIDE = 480;
|
|
84632
|
+
var CACHE_CONTROL = "private, max-age=31536000, immutable";
|
|
84633
|
+
/** The path `getRetrainFrameUrl`-style callers compose for a frame. */
|
|
84634
|
+
function retrainFramePath$1(addonId, frameId, size) {
|
|
84635
|
+
return `/addon/${addonId}/${RETRAIN_FRAME_PREFIX}/${encodeURIComponent(frameId)}?size=${size}`;
|
|
84636
|
+
}
|
|
84637
|
+
/** `{ frameId, size }` from the request URL (path after the prefix), or `null`. */
|
|
84638
|
+
function parseRetrainFrameRequest(url) {
|
|
84639
|
+
const queryAt = url.indexOf("?");
|
|
84640
|
+
const rawPath = queryAt === -1 ? url : url.slice(0, queryAt);
|
|
84641
|
+
const query = queryAt === -1 ? "" : url.slice(queryAt + 1);
|
|
84642
|
+
const segment = rawPath.replace(/^\/+/, "");
|
|
84643
|
+
if (segment === "" || segment.includes("/")) return null;
|
|
84644
|
+
let frameId;
|
|
84645
|
+
try {
|
|
84646
|
+
frameId = decodeURIComponent(segment);
|
|
84647
|
+
} catch {
|
|
84648
|
+
return null;
|
|
84649
|
+
}
|
|
84650
|
+
if (frameId === "") return null;
|
|
84651
|
+
const size = new URLSearchParams(query).get("size");
|
|
84652
|
+
if (size !== null && size !== "thumb" && size !== "full") return null;
|
|
84653
|
+
return {
|
|
84654
|
+
frameId,
|
|
84655
|
+
size: size ?? "full"
|
|
84656
|
+
};
|
|
84657
|
+
}
|
|
84658
|
+
function createRetrainFrameHandler(deps) {
|
|
84659
|
+
return async (req, res) => {
|
|
84660
|
+
const method = req.method ?? "GET";
|
|
84661
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
84662
|
+
res.writeHead(405, { allow: "GET, HEAD" });
|
|
84663
|
+
res.end();
|
|
84664
|
+
return;
|
|
84665
|
+
}
|
|
84666
|
+
const request = parseRetrainFrameRequest(req.url ?? "");
|
|
84667
|
+
if (request === null) {
|
|
84668
|
+
res.writeHead(404);
|
|
84669
|
+
res.end();
|
|
84670
|
+
return;
|
|
84671
|
+
}
|
|
84672
|
+
const etag = `"${request.frameId}:${request.size}"`;
|
|
84673
|
+
if (req.headers["if-none-match"] === etag) {
|
|
84674
|
+
res.writeHead(304, {
|
|
84675
|
+
etag,
|
|
84676
|
+
"cache-control": CACHE_CONTROL
|
|
84677
|
+
});
|
|
84678
|
+
res.end();
|
|
84679
|
+
return;
|
|
84680
|
+
}
|
|
84681
|
+
try {
|
|
84682
|
+
const stored = await deps.readFrame(request.frameId);
|
|
84683
|
+
if (stored === null) {
|
|
84684
|
+
res.writeHead(404);
|
|
84685
|
+
res.end();
|
|
84686
|
+
return;
|
|
84687
|
+
}
|
|
84688
|
+
const body = request.size === "thumb" ? await deps.resize(stored, RETRAIN_THUMB_MAX_SIDE) : stored;
|
|
84689
|
+
res.writeHead(200, {
|
|
84690
|
+
"content-type": "image/jpeg",
|
|
84691
|
+
"content-length": String(body.byteLength),
|
|
84692
|
+
"cache-control": CACHE_CONTROL,
|
|
84693
|
+
etag
|
|
84694
|
+
});
|
|
84695
|
+
if (method === "HEAD") res.end();
|
|
84696
|
+
else res.end(body);
|
|
84697
|
+
} catch (err) {
|
|
84698
|
+
deps.logger.warn("retrain frame read failed", { meta: {
|
|
84699
|
+
frameId: request.frameId,
|
|
84700
|
+
size: request.size,
|
|
84701
|
+
error: String(err)
|
|
84702
|
+
} });
|
|
84703
|
+
res.writeHead(500, { "content-type": "text/plain" });
|
|
84704
|
+
res.end("retrain frame read failed");
|
|
84705
|
+
}
|
|
84706
|
+
};
|
|
84707
|
+
}
|
|
84708
|
+
//#endregion
|
|
84449
84709
|
//#region src/pipeline-analytics/retrain/retrain-crop-cut.ts
|
|
84450
84710
|
/**
|
|
84451
84711
|
* The `crops` layout's cutter: one subject crop from a retrain frame copy.
|
|
@@ -84480,6 +84740,15 @@ function retrainCropWindow(input) {
|
|
|
84480
84740
|
height: bottom - top
|
|
84481
84741
|
};
|
|
84482
84742
|
}
|
|
84743
|
+
/** A frame re-encoded to a JPEG whose longest side is at most `maxSide` (grid thumbnails). */
|
|
84744
|
+
async function resizeRetrainJpeg(bytes, maxSide) {
|
|
84745
|
+
return sharp(bytes).resize({
|
|
84746
|
+
width: maxSide,
|
|
84747
|
+
height: maxSide,
|
|
84748
|
+
fit: "inside",
|
|
84749
|
+
withoutEnlargement: true
|
|
84750
|
+
}).jpeg({ quality: 80 }).toBuffer();
|
|
84751
|
+
}
|
|
84483
84752
|
async function cutRetrainCrop(input) {
|
|
84484
84753
|
const window = retrainCropWindow(input);
|
|
84485
84754
|
if (window === null) return null;
|
|
@@ -85582,9 +85851,27 @@ async function restageRetrainTrack(deps, input) {
|
|
|
85582
85851
|
}
|
|
85583
85852
|
//#endregion
|
|
85584
85853
|
//#region src/pipeline-analytics/retrain/retrain-service.ts
|
|
85854
|
+
/**
|
|
85855
|
+
* RetrainService — the bodies behind the eleven `pipelineAnalytics.*Retrain*`
|
|
85856
|
+
* cap methods.
|
|
85857
|
+
*
|
|
85858
|
+
* It exists as a service rather than as eleven more methods on a 7,500-line
|
|
85859
|
+
* addon class for the reason `analytics-query-facade.ts` and `track-flags.ts`
|
|
85860
|
+
* already exist: the interesting parts (the copy ordering, the assist plan, the
|
|
85861
|
+
* lifecycle preconditions) are decidable from injected contracts alone, and a
|
|
85862
|
+
* test that had to boot the addon to reach them would not be written.
|
|
85863
|
+
*
|
|
85864
|
+
* Everything durable lives in two collections and one blob prefix:
|
|
85865
|
+
* `pipeline-analytics:retrain-frames` (the copies),
|
|
85866
|
+
* `pipeline-analytics:retrain-annotations` (the ground truth), and
|
|
85867
|
+
* `retrain/<deviceId>/<trackId>/…` (the pixels). No age sweep and no owner
|
|
85868
|
+
* cascade walks any of them — that is the whole point of copying.
|
|
85869
|
+
*/
|
|
85585
85870
|
/** Default page size for the staging worklist. Staging is bounded at 500 per
|
|
85586
85871
|
* camera (D81), so this is a page, not a ceiling. */
|
|
85587
85872
|
var DEFAULT_STAGING_LIMIT = 100;
|
|
85873
|
+
/** Tries a last-write-wins save gets when it keeps losing the revision race. */
|
|
85874
|
+
var LAST_WRITE_WINS_ATTEMPTS = 3;
|
|
85588
85875
|
/** A macro class that failed validation — the write is refused, never coerced. */
|
|
85589
85876
|
var RetrainAnnotationError = class extends Error {};
|
|
85590
85877
|
/** A store frame on the wire. A recording still carries no track and no
|
|
@@ -85605,7 +85892,31 @@ function toWireFrame(f) {
|
|
|
85605
85892
|
height: f.height,
|
|
85606
85893
|
copiedAt: f.copiedAt,
|
|
85607
85894
|
...f.capturedAt !== void 0 ? { capturedAt: f.capturedAt } : {},
|
|
85608
|
-
complete: f.complete
|
|
85895
|
+
complete: f.complete,
|
|
85896
|
+
revision: f.revision ?? 0,
|
|
85897
|
+
dismissed: f.dismissed ?? []
|
|
85898
|
+
};
|
|
85899
|
+
}
|
|
85900
|
+
/**
|
|
85901
|
+
* What the operator did with the proposals on a frame (`retrain-review.ts`).
|
|
85902
|
+
*
|
|
85903
|
+
* A frame whose rows predate origins is judged against the pipeline boxes
|
|
85904
|
+
* re-derived now, and a proposal nobody kept is reported `removed` — both
|
|
85905
|
+
* flagged approximate. A frame with snapshots is judged on them alone.
|
|
85906
|
+
*/
|
|
85907
|
+
function reviewOfFrame(annotations, pipelineBoxes, dismissed) {
|
|
85908
|
+
const legacy = annotations.some((a) => !a.origin);
|
|
85909
|
+
const review = classifyRetrainReview(annotations.map((a) => ({
|
|
85910
|
+
id: a.id,
|
|
85911
|
+
bbox: a.bbox,
|
|
85912
|
+
macroClass: a.macroClass,
|
|
85913
|
+
...a.label !== void 0 ? { label: a.label } : {},
|
|
85914
|
+
kind: a.kind,
|
|
85915
|
+
origin: a.origin ?? null
|
|
85916
|
+
})), pipelineBoxes.map(pipelineBoxOrigin), dismissed, { inferRemoved: legacy });
|
|
85917
|
+
return {
|
|
85918
|
+
boxes: review.boxes,
|
|
85919
|
+
summary: summarizeReview(review)
|
|
85609
85920
|
};
|
|
85610
85921
|
}
|
|
85611
85922
|
function toWireAnnotation(a) {
|
|
@@ -85622,6 +85933,7 @@ function toWireAnnotation(a) {
|
|
|
85622
85933
|
source: a.source,
|
|
85623
85934
|
...a.assistModelId !== void 0 ? { assistModelId: a.assistModelId } : {},
|
|
85624
85935
|
...a.assistScore !== void 0 ? { assistScore: a.assistScore } : {},
|
|
85936
|
+
origin: a.origin ?? null,
|
|
85625
85937
|
...a.exportedInBatch !== void 0 ? { exportedInBatch: a.exportedInBatch } : {},
|
|
85626
85938
|
createdAt: a.createdAt
|
|
85627
85939
|
};
|
|
@@ -85703,16 +86015,24 @@ var RetrainService = class {
|
|
|
85703
86015
|
const evidence = this.deps.pipelineEvidence;
|
|
85704
86016
|
const ownTracks = /* @__PURE__ */ new Map();
|
|
85705
86017
|
const subjectBoxes = /* @__PURE__ */ new Map();
|
|
86018
|
+
const annotationsByFrame = await this.annotationsByFrame(copies);
|
|
85706
86019
|
const out = [];
|
|
85707
86020
|
for (const copy of copies) {
|
|
85708
86021
|
const wire = toWireFrame(copy);
|
|
85709
86022
|
const collectionIds = collections?.get(copy.frameId);
|
|
85710
|
-
const withCollections =
|
|
85711
|
-
...
|
|
85712
|
-
|
|
85713
|
-
|
|
86023
|
+
const withCollections = {
|
|
86024
|
+
...collections !== null ? {
|
|
86025
|
+
...wire,
|
|
86026
|
+
collectionIds: collectionIds ?? []
|
|
86027
|
+
} : wire,
|
|
86028
|
+
...this.frameUrls(copy.frameId)
|
|
86029
|
+
};
|
|
86030
|
+
const reviewOf = (boxes) => reviewOfFrame(annotationsByFrame.get(copy.frameId) ?? [], boxes, copy.dismissed ?? []);
|
|
85714
86031
|
if (evidence === void 0) {
|
|
85715
|
-
out.push(
|
|
86032
|
+
out.push({
|
|
86033
|
+
...withCollections,
|
|
86034
|
+
review: reviewOf([])
|
|
86035
|
+
});
|
|
85716
86036
|
continue;
|
|
85717
86037
|
}
|
|
85718
86038
|
const trackId = copy.origin === "track" ? copy.trackId : "";
|
|
@@ -85733,11 +86053,32 @@ var RetrainService = class {
|
|
|
85733
86053
|
});
|
|
85734
86054
|
out.push({
|
|
85735
86055
|
...withCollections,
|
|
85736
|
-
pipelineBoxes
|
|
86056
|
+
pipelineBoxes,
|
|
86057
|
+
review: reviewOf(pipelineBoxes)
|
|
85737
86058
|
});
|
|
85738
86059
|
}
|
|
85739
86060
|
return out;
|
|
85740
86061
|
}
|
|
86062
|
+
/** The URLs of a frame's pixels, when a data plane is served for them. */
|
|
86063
|
+
frameUrls(frameId) {
|
|
86064
|
+
const frameUrl = this.deps.frameUrl;
|
|
86065
|
+
if (frameUrl === void 0) return {};
|
|
86066
|
+
return {
|
|
86067
|
+
imageUrl: frameUrl(frameId, "full"),
|
|
86068
|
+
thumbnailUrl: frameUrl(frameId, "thumb")
|
|
86069
|
+
};
|
|
86070
|
+
}
|
|
86071
|
+
/** The saved annotations of these frames, grouped by frame. */
|
|
86072
|
+
async annotationsByFrame(copies) {
|
|
86073
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
86074
|
+
if (copies.length === 0) return grouped;
|
|
86075
|
+
for (const a of await this.deps.annotations.listByFrames(copies.map((c) => c.frameId))) {
|
|
86076
|
+
const list = grouped.get(a.mediaKey);
|
|
86077
|
+
if (list === void 0) grouped.set(a.mediaKey, [a]);
|
|
86078
|
+
else list.push(a);
|
|
86079
|
+
}
|
|
86080
|
+
return grouped;
|
|
86081
|
+
}
|
|
85741
86082
|
/**
|
|
85742
86083
|
* The frame a frame-scoped call names, or a refusal. The frame is the
|
|
85743
86084
|
* authority: its camera must be the caller's, and a `trackId`, when the
|
|
@@ -85824,7 +86165,10 @@ var RetrainService = class {
|
|
|
85824
86165
|
sourceKind: kindOf.get(mediaId) ?? "keyFrame",
|
|
85825
86166
|
...capturedAtOf.has(mediaId) ? { capturedAt: capturedAtOf.get(mediaId) } : {}
|
|
85826
86167
|
});
|
|
85827
|
-
if (result.kind === "copied") copied.push(
|
|
86168
|
+
if (result.kind === "copied") copied.push({
|
|
86169
|
+
...toWireFrame(result.frame),
|
|
86170
|
+
...this.frameUrls(result.frame.frameId)
|
|
86171
|
+
});
|
|
85828
86172
|
else refused.push({
|
|
85829
86173
|
sourceMediaId: result.sourceMediaId,
|
|
85830
86174
|
reason: result.reason
|
|
@@ -85851,7 +86195,10 @@ var RetrainService = class {
|
|
|
85851
86195
|
complete: input.complete
|
|
85852
86196
|
}
|
|
85853
86197
|
});
|
|
85854
|
-
return
|
|
86198
|
+
return {
|
|
86199
|
+
...toWireFrame(updated),
|
|
86200
|
+
...this.frameUrls(updated.frameId)
|
|
86201
|
+
};
|
|
85855
86202
|
}
|
|
85856
86203
|
/** Un-select: annotations first, then the copy. Never the other way round —
|
|
85857
86204
|
* an annotation whose frame is gone is unopenable and unfixable. The frame's
|
|
@@ -85992,9 +86339,16 @@ var RetrainService = class {
|
|
|
85992
86339
|
modelId: result.modelId,
|
|
85993
86340
|
stepId: plan.stepId,
|
|
85994
86341
|
minScore: plan.minScore,
|
|
85995
|
-
proposals: mapping.proposals.map((p) => {
|
|
86342
|
+
proposals: mapping.proposals.map((p, index) => {
|
|
85996
86343
|
const draft = proposalToDraft(p, owner);
|
|
85997
86344
|
return {
|
|
86345
|
+
origin: proposalOrigin("assist", index, {
|
|
86346
|
+
bbox: draft.bbox,
|
|
86347
|
+
macroClass: draft.macroClass,
|
|
86348
|
+
...draft.label !== void 0 ? { label: draft.label } : {},
|
|
86349
|
+
...draft.assistScore !== void 0 ? { score: draft.assistScore } : {},
|
|
86350
|
+
...draft.assistModelId !== void 0 ? { modelId: draft.assistModelId } : {}
|
|
86351
|
+
}),
|
|
85998
86352
|
bbox: draft.bbox,
|
|
85999
86353
|
macroClass: draft.macroClass,
|
|
86000
86354
|
...draft.label !== void 0 ? { label: draft.label } : {},
|
|
@@ -86041,9 +86395,12 @@ var RetrainService = class {
|
|
|
86041
86395
|
kind: a.kind,
|
|
86042
86396
|
source: a.source,
|
|
86043
86397
|
...a.assistModelId !== void 0 ? { assistModelId: a.assistModelId } : {},
|
|
86044
|
-
...a.assistScore !== void 0 ? { assistScore: a.assistScore } : {}
|
|
86398
|
+
...a.assistScore !== void 0 ? { assistScore: a.assistScore } : {},
|
|
86399
|
+
origin: a.origin ?? null
|
|
86045
86400
|
}));
|
|
86046
|
-
const
|
|
86401
|
+
const keptRefs = new Set(drafts.flatMap((d) => d.origin ? [d.origin.ref] : []));
|
|
86402
|
+
const dismissed = input.dismissed !== void 0 ? input.dismissed.filter((d) => !keptRefs.has(d.ref)) : void 0;
|
|
86403
|
+
const saved = await this.replaceGuarded(frame, input, drafts, dismissed);
|
|
86047
86404
|
const errors = saved.filter((a) => a.kind === "model_error").length;
|
|
86048
86405
|
this.deps.logger.info("retrain annotations saved", {
|
|
86049
86406
|
tags: { deviceId: input.deviceId },
|
|
@@ -86052,12 +86409,99 @@ var RetrainService = class {
|
|
|
86052
86409
|
trackId: frame.trackId,
|
|
86053
86410
|
frameId: input.frameId,
|
|
86054
86411
|
subjects: saved.length - errors,
|
|
86055
|
-
modelErrors: errors
|
|
86412
|
+
modelErrors: errors,
|
|
86413
|
+
dismissed: dismissed?.length
|
|
86056
86414
|
}
|
|
86057
86415
|
});
|
|
86058
86416
|
return saved.map(toWireAnnotation);
|
|
86059
86417
|
}
|
|
86060
86418
|
/**
|
|
86419
|
+
* The guarded replace. A caller that names the revision it saw gets exactly
|
|
86420
|
+
* one attempt: a different current revision is a conflict, never a retry —
|
|
86421
|
+
* the point is that it must look again. A caller that names none asked for
|
|
86422
|
+
* last-write-wins, so a race lost between our read and the transaction is
|
|
86423
|
+
* retried on the fresh revision, a bounded number of times.
|
|
86424
|
+
*/
|
|
86425
|
+
async replaceGuarded(frame, input, drafts, dismissed) {
|
|
86426
|
+
return await this.underRevision(frame, input, async (revision) => {
|
|
86427
|
+
const result = await this.deps.annotations.replaceForFrame(input.frameId, drafts, {
|
|
86428
|
+
fromRevision: revision,
|
|
86429
|
+
...dismissed !== void 0 ? { dismissed } : {}
|
|
86430
|
+
});
|
|
86431
|
+
return result.kind === "saved" ? {
|
|
86432
|
+
kind: "done",
|
|
86433
|
+
value: result.rows
|
|
86434
|
+
} : {
|
|
86435
|
+
kind: "conflict",
|
|
86436
|
+
currentRevision: result.currentRevision
|
|
86437
|
+
};
|
|
86438
|
+
});
|
|
86439
|
+
}
|
|
86440
|
+
/**
|
|
86441
|
+
* Run one revision-guarded write. `attempt` is handed the revision to guard
|
|
86442
|
+
* on and reports `done` or `conflict` (the frame moved; `null` = it is gone).
|
|
86443
|
+
*/
|
|
86444
|
+
async underRevision(frame, input, attempt) {
|
|
86445
|
+
let revision = frame.revision ?? 0;
|
|
86446
|
+
for (let tries = 0; tries < LAST_WRITE_WINS_ATTEMPTS; tries += 1) {
|
|
86447
|
+
if (input.expectedRevision !== void 0 && input.expectedRevision !== revision) this.refuseStale(input, revision, input.expectedRevision);
|
|
86448
|
+
const result = await attempt(revision);
|
|
86449
|
+
if (result.kind === "done") return result.value;
|
|
86450
|
+
if (result.currentRevision === null) throw new RetrainAnnotationError(`retrain frame ${input.frameId} vanished`);
|
|
86451
|
+
revision = result.currentRevision;
|
|
86452
|
+
if (input.expectedRevision !== void 0) this.refuseStale(input, revision, input.expectedRevision);
|
|
86453
|
+
}
|
|
86454
|
+
throw new RetrainAnnotationError(`retrain frame ${input.frameId} kept changing under the save — try again`);
|
|
86455
|
+
}
|
|
86456
|
+
/**
|
|
86457
|
+
* Give rows saved before origins existed the origin a caller reconstructed
|
|
86458
|
+
* for them — and touch nothing else (ids, creation time and the export stamp
|
|
86459
|
+
* of the row stay). Only rows that have NO origin yet are written; a row that
|
|
86460
|
+
* already has one is skipped, because a snapshot taken at accept time is
|
|
86461
|
+
* evidence and a reconstruction must never overwrite it.
|
|
86462
|
+
*/
|
|
86463
|
+
async backfillOrigins(input) {
|
|
86464
|
+
const frame = await this.requireFrame(input, "backfill-origins");
|
|
86465
|
+
const wanted = new Map(input.origins.map((o) => [o.annotationId, o.origin]));
|
|
86466
|
+
const result = await this.underRevision(frame, input, async (revision) => {
|
|
86467
|
+
const outcome = await this.deps.annotations.setOrigins(input.frameId, wanted, { fromRevision: revision });
|
|
86468
|
+
return outcome.kind === "updated" ? {
|
|
86469
|
+
kind: "done",
|
|
86470
|
+
value: outcome
|
|
86471
|
+
} : {
|
|
86472
|
+
kind: "conflict",
|
|
86473
|
+
currentRevision: outcome.currentRevision
|
|
86474
|
+
};
|
|
86475
|
+
});
|
|
86476
|
+
this.deps.logger.info("retrain origins backfilled", {
|
|
86477
|
+
tags: { deviceId: input.deviceId },
|
|
86478
|
+
meta: {
|
|
86479
|
+
frameId: input.frameId,
|
|
86480
|
+
updated: result.updated,
|
|
86481
|
+
skipped: input.origins.length - result.updated
|
|
86482
|
+
}
|
|
86483
|
+
});
|
|
86484
|
+
return {
|
|
86485
|
+
updated: result.updated,
|
|
86486
|
+
skipped: input.origins.length - result.updated,
|
|
86487
|
+
revision: result.revision
|
|
86488
|
+
};
|
|
86489
|
+
}
|
|
86490
|
+
refuseStale(input, current, expected) {
|
|
86491
|
+
this.deps.logger.warn("retrain annotations save refused — the frame moved on", {
|
|
86492
|
+
tags: { deviceId: input.deviceId },
|
|
86493
|
+
meta: {
|
|
86494
|
+
frameId: input.frameId,
|
|
86495
|
+
currentRevision: current,
|
|
86496
|
+
expectedRevision: expected
|
|
86497
|
+
}
|
|
86498
|
+
});
|
|
86499
|
+
throw new RetrainAnnotationError(retrainRevisionConflictMessage(input.frameId, {
|
|
86500
|
+
currentRevision: current,
|
|
86501
|
+
expectedRevision: expected
|
|
86502
|
+
}));
|
|
86503
|
+
}
|
|
86504
|
+
/**
|
|
86061
86505
|
* Paginated corpus list for the Frames grid. Index reads only — copies,
|
|
86062
86506
|
* annotation counts/classes, collection membership; no JPEG.
|
|
86063
86507
|
*/
|
|
@@ -111256,6 +111700,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
111256
111700
|
trainingExportDataPlane = null;
|
|
111257
111701
|
/** Handle for the retrain-export data-plane listener (dispose on shutdown). */
|
|
111258
111702
|
retrainExportDataPlane = null;
|
|
111703
|
+
/** Handle for the retrain-frame data-plane listener (dispose on shutdown). */
|
|
111704
|
+
retrainFrameDataPlane = null;
|
|
111259
111705
|
/** Handle for the debug-export data-plane listener (dispose on shutdown). */
|
|
111260
111706
|
debugExportDataPlane = null;
|
|
111261
111707
|
/** Handle for the tape-export data-plane listener (dispose on shutdown). */
|
|
@@ -113353,7 +113799,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
113353
113799
|
}),
|
|
113354
113800
|
subjectBoxes: (trackId, deviceId) => mediaStore.listSubjectBoxesByOwner("track", trackId, deviceId)
|
|
113355
113801
|
},
|
|
113356
|
-
memberships: retrainCollectionStore
|
|
113802
|
+
memberships: retrainCollectionStore,
|
|
113803
|
+
frameUrl: (frameId, size) => retrainFramePath$1(this.ctx.id, frameId, size)
|
|
113357
113804
|
});
|
|
113358
113805
|
this.retrain = retrain;
|
|
113359
113806
|
const retrainStills = new RecordedStillService(buildRecordedStillPorts({
|
|
@@ -114457,6 +114904,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
114457
114904
|
await this.serveNcArtifactPlane();
|
|
114458
114905
|
await this.serveTrainingExportPlane();
|
|
114459
114906
|
await this.serveRetrainExportPlane();
|
|
114907
|
+
await this.serveRetrainFramePlane();
|
|
114460
114908
|
await this.serveDebugExportPlane();
|
|
114461
114909
|
await this.serveTapeExportPlane();
|
|
114462
114910
|
this.ctx.logger.info("event-media data-plane served", { meta: {
|
|
@@ -114608,6 +115056,9 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
114608
115056
|
async saveRetrainAnnotations(input) {
|
|
114609
115057
|
return this.requireRetrain().saveAnnotations(input);
|
|
114610
115058
|
}
|
|
115059
|
+
async backfillRetrainOrigins(input) {
|
|
115060
|
+
return this.requireRetrain().backfillOrigins(input);
|
|
115061
|
+
}
|
|
114611
115062
|
async completeRetrainTrack(input) {
|
|
114612
115063
|
return this.requireRetrain().complete(input);
|
|
114613
115064
|
}
|
|
@@ -115088,6 +115539,31 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
115088
115539
|
}
|
|
115089
115540
|
}
|
|
115090
115541
|
/**
|
|
115542
|
+
* The retrain-frame data plane: one copied frame's JPEG (`?size=thumb|full`).
|
|
115543
|
+
* Admin-only like the cap that used to serve it as base64 (D613).
|
|
115544
|
+
*/
|
|
115545
|
+
async serveRetrainFramePlane() {
|
|
115546
|
+
try {
|
|
115547
|
+
const handler = createRetrainFrameHandler({
|
|
115548
|
+
readFrame: async (frameId) => {
|
|
115549
|
+
const frames = this.retrainFrames;
|
|
115550
|
+
if (frames === null) return null;
|
|
115551
|
+
const frame = await frames.get(frameId);
|
|
115552
|
+
return frame === null ? null : frames.readFrame(frame);
|
|
115553
|
+
},
|
|
115554
|
+
resize: resizeRetrainJpeg,
|
|
115555
|
+
logger: this.ctx.logger
|
|
115556
|
+
});
|
|
115557
|
+
this.retrainFrameDataPlane = await this.ctx.dataPlane?.serve({
|
|
115558
|
+
prefix: RETRAIN_FRAME_PREFIX,
|
|
115559
|
+
access: planeAccess("retrain-frame"),
|
|
115560
|
+
handler
|
|
115561
|
+
}) ?? null;
|
|
115562
|
+
} catch (err) {
|
|
115563
|
+
this.ctx.logger.warn("retrain-frame data-plane failed to serve", { meta: { error: errMsg(err) } });
|
|
115564
|
+
}
|
|
115565
|
+
}
|
|
115566
|
+
/**
|
|
115091
115567
|
* Plan the annotated export — INDEX READS ONLY, no blob touched.
|
|
115092
115568
|
*
|
|
115093
115569
|
* The zone used for the `zoneCrop` derivation is the bounding box of the
|
|
@@ -115869,6 +116345,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
115869
116345
|
this.trainingExportDataPlane = null;
|
|
115870
116346
|
await this.retrainExportDataPlane?.dispose();
|
|
115871
116347
|
this.retrainExportDataPlane = null;
|
|
116348
|
+
await this.retrainFrameDataPlane?.dispose();
|
|
116349
|
+
this.retrainFrameDataPlane = null;
|
|
115872
116350
|
await this.debugExportDataPlane?.dispose();
|
|
115873
116351
|
this.debugExportDataPlane = null;
|
|
115874
116352
|
await this.tapeExportDataPlane?.dispose();
|
|
@@ -116469,6 +116947,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
116469
116947
|
positionsCount: positionsCountById.get(id) ?? 1
|
|
116470
116948
|
});
|
|
116471
116949
|
if (this.startedBirths.hasStarted(key, id)) continue;
|
|
116950
|
+
if (this.suppressedBirths.isRejected(key, id)) continue;
|
|
116472
116951
|
if (appearance === "resurrection") {
|
|
116473
116952
|
log.info("track resumed", {
|
|
116474
116953
|
tags: { deviceId },
|
|
@@ -116492,6 +116971,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
116492
116971
|
}
|
|
116493
116972
|
for (const id of this.deferredBirths.deferredIds(key)) {
|
|
116494
116973
|
if (bornCandidates.some((c) => c.id === id)) continue;
|
|
116974
|
+
if (this.suppressedBirths.isRejected(key, id)) continue;
|
|
116495
116975
|
const t = result.tracked.find((x) => x.trackId === id);
|
|
116496
116976
|
if (t) bornCandidates.push({
|
|
116497
116977
|
id,
|
|
@@ -116553,6 +117033,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
116553
117033
|
} });
|
|
116554
117034
|
continue;
|
|
116555
117035
|
}
|
|
117036
|
+
if (this.suppressedBirths.isRejected(key, id)) {
|
|
117037
|
+
this.deferredBirths.resolve(key, id);
|
|
117038
|
+
this.birthEvidence.resolve(key, id);
|
|
117039
|
+
log.info("late birth verdict ignored — the birth was already suppressed", {
|
|
117040
|
+
tags: { deviceId },
|
|
117041
|
+
meta: {
|
|
117042
|
+
trackId: id,
|
|
117043
|
+
className: t.className,
|
|
117044
|
+
verdict: outcome.confirmed.has(id) ? "confirmed" : "suppressed"
|
|
117045
|
+
}
|
|
117046
|
+
});
|
|
117047
|
+
continue;
|
|
117048
|
+
}
|
|
116556
117049
|
this.recordBirthDecision({
|
|
116557
117050
|
deviceId,
|
|
116558
117051
|
trackId: id,
|