@camstack/addon-post-analysis 1.2.45 → 1.2.46

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.
@@ -12169,8 +12169,15 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
12169
12169
  failed += 1;
12170
12170
  continue;
12171
12171
  }
12172
- await deps.store(source, embedding);
12173
- rebuilt += 1;
12172
+ const outcome = await deps.store(source, embedding);
12173
+ if (outcome === "persisted") rebuilt += 1;
12174
+ else {
12175
+ failed += 1;
12176
+ deps.logger.warn("embedding rebuild wrote nothing for a track", { meta: {
12177
+ trackId,
12178
+ outcome
12179
+ } });
12180
+ }
12174
12181
  } catch (err) {
12175
12182
  failed += 1;
12176
12183
  deps.logger.warn("embedding rebuild failed for a track", { meta: {
@@ -20597,6 +20604,14 @@ var ObjectEmbeddingStore = class {
20597
20604
  * write: the provider rejects an upsert into an index it has never seen. A
20598
20605
  * dim change against a populated index is REFUSED rather than wiping it, so
20599
20606
  * this failing is the signal that the encoder model changed.
20607
+ *
20608
+ * `declaredDim` caches the declaration, and that cache is per-PROCESS while
20609
+ * the index lives in whichever addon currently provides `vector-store`. When
20610
+ * the active provider changed under a running hub the cache stayed warm
20611
+ * against an index that no longer existed, and every write from then on threw
20612
+ * `index "object-clip" was never declared` — for hours, until the addon was
20613
+ * restarted by hand. So a failed write clears the cache (see
20614
+ * {@link upsertIfBetter}) and the next one re-declares.
20600
20615
  */
20601
20616
  async ensureIndex(dim) {
20602
20617
  if (this.declaredDim === dim) return true;
@@ -20628,33 +20643,49 @@ var ObjectEmbeddingStore = class {
20628
20643
  * short-circuited, and every later pass silently wrote nothing while
20629
20644
  * reporting hundreds rebuilt. Observed live — 214 "rebuilt" moved the index
20630
20645
  * by 4 rows.
20646
+ *
20647
+ * ## Why this returns an outcome instead of `void`
20648
+ *
20649
+ * It used to return `void` and turn every failure into a warn. The rebuild
20650
+ * therefore counted a write the instant the call returned, and the second time
20651
+ * this class of bug appeared it produced `rebuilt: 704` against `indexed: 0` —
20652
+ * a progress number that was pure fiction while the real cause sat in a warn
20653
+ * nobody was reading. The caller now has to distinguish "written" from "logged
20654
+ * and dropped", because a counter that cannot reads as verification.
20631
20655
  */
20632
20656
  async upsertIfBetter(input) {
20633
- if (input.embedding.length === 0) return;
20634
- if (!await this.ensureIndex(input.embedding.length)) return;
20657
+ if (input.embedding.length === 0) return "failed";
20658
+ if (!await this.ensureIndex(input.embedding.length)) return "failed";
20635
20659
  try {
20636
20660
  if (input.force !== true) {
20637
20661
  const stored = (await this.vectorStore.getByIds.query({
20638
20662
  index: OBJECT_CLIP_INDEX,
20639
20663
  ids: [input.trackId]
20640
20664
  })).items[0]?.metadata["confidence"];
20641
- if (typeof stored === "number" && input.confidence <= stored) return;
20665
+ if (typeof stored === "number" && input.confidence <= stored) return "skipped";
20642
20666
  }
20643
- if ((await this.vectorStore.upsert.mutate({
20644
- index: "object-clip",
20667
+ const result = await this.vectorStore.upsert.mutate({
20668
+ index: OBJECT_CLIP_INDEX,
20645
20669
  items: [{
20646
20670
  id: input.trackId,
20647
20671
  vector: require_dist.encodeVectorBase64(input.embedding),
20648
20672
  metadata: metadataFor(input)
20649
20673
  }]
20650
- })).rejected > 0) this.logger.warn("object-embedding upsert rejected", {
20651
- tags: { deviceId: input.deviceId },
20652
- meta: {
20653
- trackId: input.trackId,
20654
- dim: input.embedding.length
20655
- }
20656
20674
  });
20675
+ if (result.upserted === 0) {
20676
+ this.logger.warn("object-embedding upsert rejected", {
20677
+ tags: { deviceId: input.deviceId },
20678
+ meta: {
20679
+ trackId: input.trackId,
20680
+ dim: input.embedding.length,
20681
+ rejected: result.rejected
20682
+ }
20683
+ });
20684
+ return "failed";
20685
+ }
20686
+ return "persisted";
20657
20687
  } catch (err) {
20688
+ this.declaredDim = null;
20658
20689
  this.logger.warn("ObjectEmbeddingStore.upsertIfBetter failed", {
20659
20690
  tags: { deviceId: input.deviceId },
20660
20691
  meta: {
@@ -20662,6 +20693,7 @@ var ObjectEmbeddingStore = class {
20662
20693
  error: String(err)
20663
20694
  }
20664
20695
  });
20696
+ return "failed";
20665
20697
  }
20666
20698
  }
20667
20699
  /**
@@ -27113,20 +27145,18 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
27113
27145
  height
27114
27146
  })).embedding;
27115
27147
  },
27116
- store: async (source, embedding) => {
27117
- await store.upsertIfBetter({
27118
- trackId: source.trackId,
27119
- deviceId: source.deviceId,
27120
- timestamp: source.timestamp,
27121
- className: source.className,
27122
- embedding,
27123
- modelId,
27124
- confidence: source.confidence,
27125
- force: true,
27126
- ...source.mediaKey !== void 0 ? { mediaKey: source.mediaKey } : {},
27127
- ...source.keyFrameMediaKey !== void 0 ? { keyFrameMediaKey: source.keyFrameMediaKey } : {}
27128
- });
27129
- },
27148
+ store: async (source, embedding) => store.upsertIfBetter({
27149
+ trackId: source.trackId,
27150
+ deviceId: source.deviceId,
27151
+ timestamp: source.timestamp,
27152
+ className: source.className,
27153
+ embedding,
27154
+ modelId,
27155
+ confidence: source.confidence,
27156
+ force: true,
27157
+ ...source.mediaKey !== void 0 ? { mediaKey: source.mediaKey } : {},
27158
+ ...source.keyFrameMediaKey !== void 0 ? { keyFrameMediaKey: source.keyFrameMediaKey } : {}
27159
+ }),
27130
27160
  logger: {
27131
27161
  info: (message, extras) => this.ctx.logger.info(message, extras),
27132
27162
  warn: (message, extras) => this.ctx.logger.warn(message, extras)
@@ -12163,8 +12163,15 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
12163
12163
  failed += 1;
12164
12164
  continue;
12165
12165
  }
12166
- await deps.store(source, embedding);
12167
- rebuilt += 1;
12166
+ const outcome = await deps.store(source, embedding);
12167
+ if (outcome === "persisted") rebuilt += 1;
12168
+ else {
12169
+ failed += 1;
12170
+ deps.logger.warn("embedding rebuild wrote nothing for a track", { meta: {
12171
+ trackId,
12172
+ outcome
12173
+ } });
12174
+ }
12168
12175
  } catch (err) {
12169
12176
  failed += 1;
12170
12177
  deps.logger.warn("embedding rebuild failed for a track", { meta: {
@@ -20591,6 +20598,14 @@ var ObjectEmbeddingStore = class {
20591
20598
  * write: the provider rejects an upsert into an index it has never seen. A
20592
20599
  * dim change against a populated index is REFUSED rather than wiping it, so
20593
20600
  * this failing is the signal that the encoder model changed.
20601
+ *
20602
+ * `declaredDim` caches the declaration, and that cache is per-PROCESS while
20603
+ * the index lives in whichever addon currently provides `vector-store`. When
20604
+ * the active provider changed under a running hub the cache stayed warm
20605
+ * against an index that no longer existed, and every write from then on threw
20606
+ * `index "object-clip" was never declared` — for hours, until the addon was
20607
+ * restarted by hand. So a failed write clears the cache (see
20608
+ * {@link upsertIfBetter}) and the next one re-declares.
20594
20609
  */
20595
20610
  async ensureIndex(dim) {
20596
20611
  if (this.declaredDim === dim) return true;
@@ -20622,33 +20637,49 @@ var ObjectEmbeddingStore = class {
20622
20637
  * short-circuited, and every later pass silently wrote nothing while
20623
20638
  * reporting hundreds rebuilt. Observed live — 214 "rebuilt" moved the index
20624
20639
  * by 4 rows.
20640
+ *
20641
+ * ## Why this returns an outcome instead of `void`
20642
+ *
20643
+ * It used to return `void` and turn every failure into a warn. The rebuild
20644
+ * therefore counted a write the instant the call returned, and the second time
20645
+ * this class of bug appeared it produced `rebuilt: 704` against `indexed: 0` —
20646
+ * a progress number that was pure fiction while the real cause sat in a warn
20647
+ * nobody was reading. The caller now has to distinguish "written" from "logged
20648
+ * and dropped", because a counter that cannot reads as verification.
20625
20649
  */
20626
20650
  async upsertIfBetter(input) {
20627
- if (input.embedding.length === 0) return;
20628
- if (!await this.ensureIndex(input.embedding.length)) return;
20651
+ if (input.embedding.length === 0) return "failed";
20652
+ if (!await this.ensureIndex(input.embedding.length)) return "failed";
20629
20653
  try {
20630
20654
  if (input.force !== true) {
20631
20655
  const stored = (await this.vectorStore.getByIds.query({
20632
20656
  index: OBJECT_CLIP_INDEX,
20633
20657
  ids: [input.trackId]
20634
20658
  })).items[0]?.metadata["confidence"];
20635
- if (typeof stored === "number" && input.confidence <= stored) return;
20659
+ if (typeof stored === "number" && input.confidence <= stored) return "skipped";
20636
20660
  }
20637
- if ((await this.vectorStore.upsert.mutate({
20638
- index: "object-clip",
20661
+ const result = await this.vectorStore.upsert.mutate({
20662
+ index: OBJECT_CLIP_INDEX,
20639
20663
  items: [{
20640
20664
  id: input.trackId,
20641
20665
  vector: encodeVectorBase64(input.embedding),
20642
20666
  metadata: metadataFor(input)
20643
20667
  }]
20644
- })).rejected > 0) this.logger.warn("object-embedding upsert rejected", {
20645
- tags: { deviceId: input.deviceId },
20646
- meta: {
20647
- trackId: input.trackId,
20648
- dim: input.embedding.length
20649
- }
20650
20668
  });
20669
+ if (result.upserted === 0) {
20670
+ this.logger.warn("object-embedding upsert rejected", {
20671
+ tags: { deviceId: input.deviceId },
20672
+ meta: {
20673
+ trackId: input.trackId,
20674
+ dim: input.embedding.length,
20675
+ rejected: result.rejected
20676
+ }
20677
+ });
20678
+ return "failed";
20679
+ }
20680
+ return "persisted";
20651
20681
  } catch (err) {
20682
+ this.declaredDim = null;
20652
20683
  this.logger.warn("ObjectEmbeddingStore.upsertIfBetter failed", {
20653
20684
  tags: { deviceId: input.deviceId },
20654
20685
  meta: {
@@ -20656,6 +20687,7 @@ var ObjectEmbeddingStore = class {
20656
20687
  error: String(err)
20657
20688
  }
20658
20689
  });
20690
+ return "failed";
20659
20691
  }
20660
20692
  }
20661
20693
  /**
@@ -27107,20 +27139,18 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
27107
27139
  height
27108
27140
  })).embedding;
27109
27141
  },
27110
- store: async (source, embedding) => {
27111
- await store.upsertIfBetter({
27112
- trackId: source.trackId,
27113
- deviceId: source.deviceId,
27114
- timestamp: source.timestamp,
27115
- className: source.className,
27116
- embedding,
27117
- modelId,
27118
- confidence: source.confidence,
27119
- force: true,
27120
- ...source.mediaKey !== void 0 ? { mediaKey: source.mediaKey } : {},
27121
- ...source.keyFrameMediaKey !== void 0 ? { keyFrameMediaKey: source.keyFrameMediaKey } : {}
27122
- });
27123
- },
27142
+ store: async (source, embedding) => store.upsertIfBetter({
27143
+ trackId: source.trackId,
27144
+ deviceId: source.deviceId,
27145
+ timestamp: source.timestamp,
27146
+ className: source.className,
27147
+ embedding,
27148
+ modelId,
27149
+ confidence: source.confidence,
27150
+ force: true,
27151
+ ...source.mediaKey !== void 0 ? { mediaKey: source.mediaKey } : {},
27152
+ ...source.keyFrameMediaKey !== void 0 ? { keyFrameMediaKey: source.keyFrameMediaKey } : {}
27153
+ }),
27124
27154
  logger: {
27125
27155
  info: (message, extras) => this.ctx.logger.info(message, extras),
27126
27156
  warn: (message, extras) => this.ctx.logger.warn(message, extras)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-post-analysis",
3
- "version": "1.2.45",
3
+ "version": "1.2.46",
4
4
  "description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
5
5
  "keywords": [
6
6
  "camstack",
@@ -87,6 +87,12 @@
87
87
  },
88
88
  {
89
89
  "name": "videoclips"
90
+ },
91
+ {
92
+ "name": "face-gallery"
93
+ },
94
+ {
95
+ "name": "plate-gallery"
90
96
  }
91
97
  ],
92
98
  "storageLocations": [