@semiont/make-meaning 0.5.11 → 0.5.13
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/README.md +10 -10
- package/dist/index.d.ts +140 -107
- package/dist/index.js +532 -618
- package/dist/index.js.map +1 -1
- package/dist/smelter-main.js +144 -30
- package/dist/smelter-main.js.map +1 -1
- package/dist/weaver-main.js +10746 -0
- package/dist/weaver-main.js.map +1 -0
- package/package.json +17 -13
package/dist/smelter-main.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createTomlConfigLoader, accessToken, baseUrl as baseUrl$1, burstBuffer, errField, resourceId, textExtractionOf, decodeRepresentation, busRequest, getResourceEntityTypes, getTargetSelector, getExactText, annotationId, getPrimaryMediaType, getPrimaryRepresentation } from '@semiont/core';
|
|
1
|
+
import { createTomlConfigLoader, accessToken, baseUrl as baseUrl$1, retryWithBackoff, isTransientFetchError, STARTUP_FETCH_RETRY, burstBuffer, errField, resourceId, textExtractionOf, decodeRepresentation, busRequest, getResourceEntityTypes, getTargetSelector, getExactText, annotationId, getPrimaryMediaType, getPrimaryRepresentation } from '@semiont/core';
|
|
2
2
|
import { calculateChecksum } from '@semiont/content';
|
|
3
3
|
import { createEmbeddingProvider, createVectorStore, chunkText } from '@semiont/vectors';
|
|
4
4
|
import { registerVectorIndexSizeProvider, withActorSpan } from '@semiont/observability';
|
|
@@ -9732,8 +9732,11 @@ var SMELTER_CHANNELS = [
|
|
|
9732
9732
|
"yield:updated",
|
|
9733
9733
|
"yield:representation-added",
|
|
9734
9734
|
"mark:archived",
|
|
9735
|
+
"mark:unarchived",
|
|
9735
9736
|
"mark:added",
|
|
9736
|
-
"mark:removed"
|
|
9737
|
+
"mark:removed",
|
|
9738
|
+
"mark:entity-tag-added",
|
|
9739
|
+
"mark:entity-tag-removed"
|
|
9737
9740
|
];
|
|
9738
9741
|
function createSmelterActorStateUnit(options) {
|
|
9739
9742
|
const { bus } = options;
|
|
@@ -9782,8 +9785,20 @@ function partitionByType(events) {
|
|
|
9782
9785
|
}
|
|
9783
9786
|
|
|
9784
9787
|
// src/smelter.ts
|
|
9788
|
+
function sameStringSet(a, b) {
|
|
9789
|
+
if (a.length !== b.length) return false;
|
|
9790
|
+
const set = new Set(a);
|
|
9791
|
+
return b.every((t) => set.has(t));
|
|
9792
|
+
}
|
|
9793
|
+
var WORK_ITEM_TYPES = /* @__PURE__ */ new Set([
|
|
9794
|
+
"smelt:embed",
|
|
9795
|
+
"smelt:restamp",
|
|
9796
|
+
"smelt:purge",
|
|
9797
|
+
"smelt:embed-annotation",
|
|
9798
|
+
"smelt:purge-annotation"
|
|
9799
|
+
]);
|
|
9785
9800
|
function isWorkItem(input) {
|
|
9786
|
-
return input.type
|
|
9801
|
+
return WORK_ITEM_TYPES.has(input.type);
|
|
9787
9802
|
}
|
|
9788
9803
|
var Smelter = class _Smelter {
|
|
9789
9804
|
constructor(events$, vectorStore, embeddingProvider, content, bus, chunkingConfig2, timing, logger2) {
|
|
@@ -9947,16 +9962,26 @@ var Smelter = class _Smelter {
|
|
|
9947
9962
|
case "mark:archived":
|
|
9948
9963
|
await this.handleResourceArchived(event);
|
|
9949
9964
|
break;
|
|
9965
|
+
case "mark:unarchived":
|
|
9966
|
+
await this.handleResourceUnarchived(event);
|
|
9967
|
+
break;
|
|
9950
9968
|
case "mark:added":
|
|
9951
9969
|
await this.handleAnnotationAdded(event);
|
|
9952
9970
|
break;
|
|
9953
9971
|
case "mark:removed":
|
|
9954
9972
|
await this.handleAnnotationRemoved(event);
|
|
9955
9973
|
break;
|
|
9974
|
+
case "mark:entity-tag-added":
|
|
9975
|
+
case "mark:entity-tag-removed":
|
|
9976
|
+
await this.restampResource(event);
|
|
9977
|
+
break;
|
|
9956
9978
|
// Reconcile work items — same handlers, distinct provenance.
|
|
9957
9979
|
case "smelt:embed":
|
|
9958
9980
|
await this.embedResource(event, "Reconcile-indexed resource");
|
|
9959
9981
|
break;
|
|
9982
|
+
case "smelt:restamp":
|
|
9983
|
+
await this.restampResource(event);
|
|
9984
|
+
break;
|
|
9960
9985
|
case "smelt:purge":
|
|
9961
9986
|
await this.handleResourcePurge(event);
|
|
9962
9987
|
break;
|
|
@@ -9968,6 +9993,21 @@ var Smelter = class _Smelter {
|
|
|
9968
9993
|
break;
|
|
9969
9994
|
}
|
|
9970
9995
|
}
|
|
9996
|
+
/**
|
|
9997
|
+
* Payload-only stamp refresh: re-read the resource's CURRENT entity types
|
|
9998
|
+
* (one code path — `resolveEntityTypes` — so any prior drift self-corrects
|
|
9999
|
+
* on first touch) and rewrite the stamp on its existing points. Never calls
|
|
10000
|
+
* the embedding provider (S13): content is unchanged by definition on every
|
|
10001
|
+
* path that lands here. A resource with no points is a no-op — the stamp
|
|
10002
|
+
* rides the next embed.
|
|
10003
|
+
*/
|
|
10004
|
+
async restampResource(event) {
|
|
10005
|
+
const rid = event.resourceId;
|
|
10006
|
+
if (!rid) return;
|
|
10007
|
+
const entityTypes = await this.resolveEntityTypes(rid);
|
|
10008
|
+
await this.vectorStore.updateResourceEntityTypes(resourceId(rid), entityTypes);
|
|
10009
|
+
this.logger.info("Restamped resource entity types", { resourceId: rid, entityTypes });
|
|
10010
|
+
}
|
|
9971
10011
|
async handleResourcePurge(event) {
|
|
9972
10012
|
const rid = event.resourceId;
|
|
9973
10013
|
if (!rid) return;
|
|
@@ -9985,16 +10025,30 @@ var Smelter = class _Smelter {
|
|
|
9985
10025
|
async fetchEmbeddableText(resourceId$1) {
|
|
9986
10026
|
try {
|
|
9987
10027
|
const { data, contentType } = await this.content.getBinary(resourceId(resourceId$1));
|
|
10028
|
+
const bytes = Buffer.from(data);
|
|
10029
|
+
const checksum = calculateChecksum(bytes);
|
|
9988
10030
|
if (textExtractionOf(contentType) !== "decode") {
|
|
9989
10031
|
this.logger.debug("Skipping resource that does not decode as text", { resourceId: resourceId$1, contentType });
|
|
9990
|
-
return
|
|
10032
|
+
return { kind: "skipped", checksum };
|
|
9991
10033
|
}
|
|
9992
|
-
const bytes = Buffer.from(data);
|
|
9993
10034
|
const text = decodeRepresentation(bytes, contentType);
|
|
9994
|
-
return text.trim() ? { text, checksum
|
|
10035
|
+
return text.trim() ? { kind: "text", text, checksum } : { kind: "skipped", checksum };
|
|
9995
10036
|
} catch (error) {
|
|
9996
10037
|
this.logger.warn("Content unavailable for embedding", { resourceId: resourceId$1, error: errField(error) });
|
|
9997
|
-
return
|
|
10038
|
+
return { kind: "unavailable" };
|
|
10039
|
+
}
|
|
10040
|
+
}
|
|
10041
|
+
/**
|
|
10042
|
+
* The Smelter's single outbound signal (SMELTER-AXIOMS D3 as amended by
|
|
10043
|
+
* SMELTER-INDEX-SYNC): a per-resource decision report for the barrier
|
|
10044
|
+
* fold. Best-effort — waiters degrade to their bounded timeout; a signal
|
|
10045
|
+
* failure must never fail the embed.
|
|
10046
|
+
*/
|
|
10047
|
+
async emitSettled(resourceId, contentChecksum, outcome) {
|
|
10048
|
+
try {
|
|
10049
|
+
await this.bus.emit("smelt:settled", { resourceId, contentChecksum, outcome });
|
|
10050
|
+
} catch (error) {
|
|
10051
|
+
this.logger.warn("Failed to emit smelt:settled", { resourceId, outcome, error: errField(error) });
|
|
9998
10052
|
}
|
|
9999
10053
|
}
|
|
10000
10054
|
/**
|
|
@@ -10018,9 +10072,16 @@ var Smelter = class _Smelter {
|
|
|
10018
10072
|
const rid = event.resourceId;
|
|
10019
10073
|
if (!rid) return;
|
|
10020
10074
|
const fetched = await this.fetchEmbeddableText(rid);
|
|
10021
|
-
if (
|
|
10075
|
+
if (fetched.kind === "unavailable") return;
|
|
10076
|
+
if (fetched.kind === "skipped") {
|
|
10077
|
+
await this.emitSettled(rid, fetched.checksum, "skipped");
|
|
10078
|
+
return;
|
|
10079
|
+
}
|
|
10022
10080
|
const chunks = chunkText(fetched.text, this.chunkingConfig);
|
|
10023
|
-
if (chunks.length === 0)
|
|
10081
|
+
if (chunks.length === 0) {
|
|
10082
|
+
await this.emitSettled(rid, fetched.checksum, "skipped");
|
|
10083
|
+
return;
|
|
10084
|
+
}
|
|
10024
10085
|
const entityTypes = await this.resolveEntityTypes(rid);
|
|
10025
10086
|
const embeddings = await this.embeddingProvider.embedBatch(chunks);
|
|
10026
10087
|
const embeddingChunks = chunks.map((t, i) => ({
|
|
@@ -10029,6 +10090,7 @@ var Smelter = class _Smelter {
|
|
|
10029
10090
|
embedding: embeddings[i]
|
|
10030
10091
|
}));
|
|
10031
10092
|
await this.vectorStore.upsertResourceVectors(resourceId(rid), embeddingChunks, fetched.checksum, entityTypes);
|
|
10093
|
+
await this.emitSettled(rid, fetched.checksum, "indexed");
|
|
10032
10094
|
this.logger.info(logMessage, { resourceId: rid, chunks: chunks.length });
|
|
10033
10095
|
}
|
|
10034
10096
|
async handleResourceArchived(event) {
|
|
@@ -10038,11 +10100,34 @@ var Smelter = class _Smelter {
|
|
|
10038
10100
|
await this.vectorStore.deleteAnnotationVectorsForResource(resourceId(rid));
|
|
10039
10101
|
this.logger.info("Deleted vectors for archived resource", { resourceId: rid });
|
|
10040
10102
|
}
|
|
10103
|
+
/**
|
|
10104
|
+
* Restore what `handleResourceArchived` deleted, from CURRENT state: the
|
|
10105
|
+
* resource's vectors (media-gated, full-replace) and its current exact-text
|
|
10106
|
+
* annotations — the same catalog read `reconcile()` uses, so the live path
|
|
10107
|
+
* and a restart agree (bugs/smelter-misses-unarchive.md).
|
|
10108
|
+
*/
|
|
10109
|
+
async handleResourceUnarchived(event) {
|
|
10110
|
+
const rid = event.resourceId;
|
|
10111
|
+
if (!rid) return;
|
|
10112
|
+
await this.embedResource(event, "Re-embedded unarchived resource");
|
|
10113
|
+
const { annotations } = await busRequest(
|
|
10114
|
+
this.bus,
|
|
10115
|
+
"browse:annotations-requested",
|
|
10116
|
+
{ resourceId: rid }
|
|
10117
|
+
);
|
|
10118
|
+
for (const annotation of annotations) {
|
|
10119
|
+
await this.indexAnnotation(rid, annotation);
|
|
10120
|
+
}
|
|
10121
|
+
}
|
|
10041
10122
|
async handleAnnotationAdded(event) {
|
|
10042
10123
|
const annotation = event.payload.annotation;
|
|
10043
10124
|
if (!annotation?.id) return;
|
|
10044
10125
|
const rid = event.resourceId;
|
|
10045
10126
|
if (!rid) return;
|
|
10127
|
+
await this.indexAnnotation(rid, annotation);
|
|
10128
|
+
}
|
|
10129
|
+
async indexAnnotation(rid, annotation) {
|
|
10130
|
+
if (!annotation.id) return;
|
|
10046
10131
|
const selector = getTargetSelector(annotation.target);
|
|
10047
10132
|
const exactText = getExactText(selector);
|
|
10048
10133
|
if (!exactText?.trim()) return;
|
|
@@ -10076,9 +10161,16 @@ var Smelter = class _Smelter {
|
|
|
10076
10161
|
const rid = event.resourceId;
|
|
10077
10162
|
if (!rid) continue;
|
|
10078
10163
|
const fetched = await this.fetchEmbeddableText(rid);
|
|
10079
|
-
if (
|
|
10164
|
+
if (fetched.kind === "unavailable") continue;
|
|
10165
|
+
if (fetched.kind === "skipped") {
|
|
10166
|
+
await this.emitSettled(rid, fetched.checksum, "skipped");
|
|
10167
|
+
continue;
|
|
10168
|
+
}
|
|
10080
10169
|
const chunks = chunkText(fetched.text, this.chunkingConfig);
|
|
10081
|
-
if (chunks.length === 0)
|
|
10170
|
+
if (chunks.length === 0) {
|
|
10171
|
+
await this.emitSettled(rid, fetched.checksum, "skipped");
|
|
10172
|
+
continue;
|
|
10173
|
+
}
|
|
10082
10174
|
const entityTypes = await this.resolveEntityTypes(rid);
|
|
10083
10175
|
resourceData.push({ rid: resourceId(rid), chunks, checksum: fetched.checksum, entityTypes });
|
|
10084
10176
|
allChunks.push(...chunks);
|
|
@@ -10093,6 +10185,7 @@ var Smelter = class _Smelter {
|
|
|
10093
10185
|
embedding: allEmbeddings[offset + i]
|
|
10094
10186
|
}));
|
|
10095
10187
|
await this.vectorStore.upsertResourceVectors(rid, embeddingChunks, checksum, entityTypes);
|
|
10188
|
+
await this.emitSettled(String(rid), checksum, "indexed");
|
|
10096
10189
|
this.logger.info("Batch-indexed resource", { resourceId: String(rid), chunks: chunks.length });
|
|
10097
10190
|
offset += chunks.length;
|
|
10098
10191
|
}
|
|
@@ -10165,7 +10258,7 @@ var Smelter = class _Smelter {
|
|
|
10165
10258
|
this._reconcileState = { phase: "running" };
|
|
10166
10259
|
try {
|
|
10167
10260
|
const [indexedResources, indexedAnnotations] = await Promise.all([
|
|
10168
|
-
this.vectorStore.
|
|
10261
|
+
this.vectorStore.listResourceStamps(),
|
|
10169
10262
|
this.vectorStore.listAnnotationIds()
|
|
10170
10263
|
]);
|
|
10171
10264
|
const resources = await this.listAllResources();
|
|
@@ -10178,18 +10271,24 @@ var Smelter = class _Smelter {
|
|
|
10178
10271
|
for (const resource of resources) {
|
|
10179
10272
|
const mediaType = getPrimaryMediaType(resource);
|
|
10180
10273
|
if (resource["@id"] && mediaType && textExtractionOf(mediaType) === "decode") {
|
|
10181
|
-
embeddable.set(resource["@id"],
|
|
10274
|
+
embeddable.set(resource["@id"], {
|
|
10275
|
+
checksum: getPrimaryRepresentation(resource)?.checksum,
|
|
10276
|
+
entityTypes: getResourceEntityTypes(resource)
|
|
10277
|
+
});
|
|
10182
10278
|
}
|
|
10183
10279
|
}
|
|
10184
10280
|
const work = [];
|
|
10185
10281
|
for (const rid of indexedResources.keys()) {
|
|
10186
10282
|
if (!embeddable.has(rid)) work.push({ type: "smelt:purge", resourceId: rid, payload: {} });
|
|
10187
10283
|
}
|
|
10188
|
-
for (const [rid,
|
|
10189
|
-
|
|
10284
|
+
for (const [rid, catalog] of embeddable) {
|
|
10285
|
+
const indexed = indexedResources.get(rid);
|
|
10286
|
+
if (!indexed) {
|
|
10190
10287
|
work.push({ type: "smelt:embed", resourceId: rid, payload: {} });
|
|
10191
|
-
} else if (
|
|
10288
|
+
} else if (catalog.checksum !== void 0 && indexed.contentChecksum !== catalog.checksum) {
|
|
10192
10289
|
work.push({ type: "smelt:embed", resourceId: rid, payload: {} });
|
|
10290
|
+
} else if (!sameStringSet(indexed.entityTypes, catalog.entityTypes)) {
|
|
10291
|
+
work.push({ type: "smelt:restamp", resourceId: rid, payload: {} });
|
|
10193
10292
|
}
|
|
10194
10293
|
}
|
|
10195
10294
|
const liveAnnotationIds = /* @__PURE__ */ new Set();
|
|
@@ -10218,6 +10317,7 @@ var Smelter = class _Smelter {
|
|
|
10218
10317
|
await this.drain(work);
|
|
10219
10318
|
const summary = {
|
|
10220
10319
|
resourcesEmbedded: work.filter((w) => w.type === "smelt:embed").length,
|
|
10320
|
+
resourcesRestamped: work.filter((w) => w.type === "smelt:restamp").length,
|
|
10221
10321
|
resourceVectorsDeleted: work.filter((w) => w.type === "smelt:purge").length,
|
|
10222
10322
|
annotationsEmbedded: work.filter((w) => w.type === "smelt:embed-annotation").length,
|
|
10223
10323
|
annotationVectorsDeleted: work.filter((w) => w.type === "smelt:purge-annotation").length
|
|
@@ -10306,20 +10406,34 @@ async function authenticate() {
|
|
|
10306
10406
|
logger.warn("No SEMIONT_WORKER_SECRET set \u2014 using empty token");
|
|
10307
10407
|
return "";
|
|
10308
10408
|
}
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10409
|
+
return retryWithBackoff(
|
|
10410
|
+
async () => {
|
|
10411
|
+
const response = await fetch(`${baseUrl}/api/tokens/agent`, {
|
|
10412
|
+
method: "POST",
|
|
10413
|
+
headers: { "Content-Type": "application/json" },
|
|
10414
|
+
body: JSON.stringify({
|
|
10415
|
+
secret: workerSecret,
|
|
10416
|
+
provider: embeddingType,
|
|
10417
|
+
model: embeddingModel
|
|
10418
|
+
})
|
|
10419
|
+
});
|
|
10420
|
+
if (!response.ok) {
|
|
10421
|
+
throw new Error(`Authentication failed: ${response.status} ${response.statusText}`);
|
|
10422
|
+
}
|
|
10423
|
+
const { token } = await response.json();
|
|
10424
|
+
return token;
|
|
10425
|
+
},
|
|
10426
|
+
isTransientFetchError,
|
|
10427
|
+
STARTUP_FETCH_RETRY,
|
|
10428
|
+
({ attempt, attempts, delayMs, error }) => {
|
|
10429
|
+
logger.warn("Backend unreachable, retrying authentication", {
|
|
10430
|
+
attempt,
|
|
10431
|
+
attempts,
|
|
10432
|
+
retryInMs: delayMs,
|
|
10433
|
+
error: error instanceof Error ? error.message : String(error)
|
|
10434
|
+
});
|
|
10435
|
+
}
|
|
10436
|
+
);
|
|
10323
10437
|
}
|
|
10324
10438
|
async function main() {
|
|
10325
10439
|
const { initObservabilityNode } = await import('@semiont/observability/node');
|