@semiont/jobs 0.5.32 → 0.5.34
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/index.d.ts +51 -60
- package/dist/index.js +227 -143
- package/dist/index.js.map +1 -1
- package/dist/worker-main.js +326 -179
- package/dist/worker-main.js.map +1 -1
- package/package.json +8 -8
package/dist/worker-main.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { replyChannelsFor, createTomlConfigLoader, didToAgent, baseUrl, STARTUP_FETCH_RETRY, retryWithBackoff, isTransientFetchError, isObject, isString, isNumber, busRequest, isArray, resourceId, getPrimaryMediaType, isGenerationJobParams, capabilitiesOf, assembleAnnotation, findClaimSpan, textSourceOf, yieldsGeometryOf, decodeRepresentation, GENERATABLE_MEDIA_TYPES, locate, createFragmentSelector, deriveViews, estimateTokens, chunkText, reconcileSelector, getLocaleEnglishName } from '@semiont/core';
|
|
1
|
+
import { replyChannelsFor, createTomlConfigLoader, didToAgent, baseUrl, STARTUP_FETCH_RETRY, retryWithBackoff, isTransientFetchError, isObject, isString, isNumber, busRequest, isArray, resourceId, getPrimaryMediaType, isGenerationJobParams, capabilitiesOf, assembleAnnotation, findClaimSpan, textSourceOf, yieldsGeometryOf, decodeRepresentation, GENERATABLE_MEDIA_TYPES, BusRequestError, locate, createFragmentSelector, deriveViews, annotationId, estimateTokens, chunkText, reconcileSelector, getLocaleEnglishName } from '@semiont/core';
|
|
2
2
|
import { archivistContentReads, extractPdfTextLayer, withinByteBudget, MAX_PDF_BYTES } from '@semiont/content';
|
|
3
3
|
import { withSpan, SpanKind, recordJobOutcome, recordAnchorOutcome, recordDetectionCall } from '@semiont/observability';
|
|
4
4
|
import { createInferenceClient, StructuredReadError } from '@semiont/inference';
|
|
@@ -9476,12 +9476,16 @@ var YieldCollapseError = class extends DeterministicJobError {
|
|
|
9476
9476
|
/** What the flagged extraction DID find — every span write-time-verified,
|
|
9477
9477
|
* so discarding it at the floor would add loss on top of the under-report.
|
|
9478
9478
|
* Carried on the error because the flag site cannot know whether descent
|
|
9479
|
-
* remains possible; the floor is the subdivider's knowledge.
|
|
9480
|
-
|
|
9479
|
+
* remains possible; the floor is the subdivider's knowledge. The verdict
|
|
9480
|
+
* rides beside it so an acceptance can report evidence, not a message
|
|
9481
|
+
* string. */
|
|
9482
|
+
constructor(message, salvage, verdict) {
|
|
9481
9483
|
super(message);
|
|
9482
9484
|
this.salvage = salvage;
|
|
9485
|
+
this.verdict = verdict;
|
|
9483
9486
|
}
|
|
9484
9487
|
salvage;
|
|
9488
|
+
verdict;
|
|
9485
9489
|
name = "YieldCollapseError";
|
|
9486
9490
|
};
|
|
9487
9491
|
function deriveDetectionBudget(limits, scaffoldTokens, typesPerCall) {
|
|
@@ -9535,7 +9539,7 @@ function outcomeOf(error) {
|
|
|
9535
9539
|
if (error instanceof InferenceTimeoutError) return "timeout";
|
|
9536
9540
|
return "error";
|
|
9537
9541
|
}
|
|
9538
|
-
async function callChunkSubdividing(label, chunk, chunking, call, logger2) {
|
|
9542
|
+
async function callChunkSubdividing(label, chunk, chunking, call, logger2, onUnderReport, onCounted) {
|
|
9539
9543
|
async function recorded(piece, depth, reroll) {
|
|
9540
9544
|
const start = performance.now();
|
|
9541
9545
|
try {
|
|
@@ -9550,6 +9554,7 @@ async function callChunkSubdividing(label, chunk, chunking, call, logger2) {
|
|
|
9550
9554
|
outcome: "success",
|
|
9551
9555
|
...result.usage ? { inputTokens: result.usage.inputTokens, outputTokens: result.usage.outputTokens } : {}
|
|
9552
9556
|
});
|
|
9557
|
+
if (result.counted !== void 0) onCounted?.(result.counted);
|
|
9553
9558
|
return result;
|
|
9554
9559
|
} catch (error) {
|
|
9555
9560
|
recordDetectionCall({
|
|
@@ -9580,6 +9585,8 @@ async function callChunkSubdividing(label, chunk, chunking, call, logger2) {
|
|
|
9580
9585
|
salvaged: error.salvage.length,
|
|
9581
9586
|
error: error.message
|
|
9582
9587
|
});
|
|
9588
|
+
onUnderReport?.(error.verdict);
|
|
9589
|
+
onCounted?.(error.verdict.counted);
|
|
9583
9590
|
return error.salvage;
|
|
9584
9591
|
}
|
|
9585
9592
|
if (!truncation(error)) throw error;
|
|
@@ -10103,7 +10110,7 @@ function logAnchorMethod(motivation, exact, anchorMethod) {
|
|
|
10103
10110
|
}
|
|
10104
10111
|
|
|
10105
10112
|
// src/workers/annotation-detection.ts
|
|
10106
|
-
async function detectInChunks(client, content, buildPrompt, motivation, elementSchema, parse, onActivity) {
|
|
10113
|
+
async function detectInChunks(client, content, buildPrompt, motivation, elementSchema, parse, onActivity, onChunkResults) {
|
|
10107
10114
|
const limits = await client.limits();
|
|
10108
10115
|
const scaffoldTokens = estimateTokens(buildPrompt(""));
|
|
10109
10116
|
const { chunking, outputBudget } = deriveDetectionBudget(limits, scaffoldTokens, 1);
|
|
@@ -10123,7 +10130,9 @@ async function detectInChunks(client, content, buildPrompt, motivation, elementS
|
|
|
10123
10130
|
assertNotTruncated(response, `${motivation} detection`, i + 1, chunks.length, outputBudget);
|
|
10124
10131
|
return { items: response.items, ...response.usage ? { usage: response.usage } : {} };
|
|
10125
10132
|
});
|
|
10126
|
-
|
|
10133
|
+
const fromChunk = parse(items);
|
|
10134
|
+
collected.push(...fromChunk);
|
|
10135
|
+
await onChunkResults?.(fromChunk);
|
|
10127
10136
|
if (i < chunks.length - 1) {
|
|
10128
10137
|
onActivity?.(i + 1, chunks.length);
|
|
10129
10138
|
}
|
|
@@ -10139,7 +10148,7 @@ var AnnotationDetection = class {
|
|
|
10139
10148
|
* (source-resource locale). See `types.ts` "Locale conventions" for the
|
|
10140
10149
|
* full discussion.
|
|
10141
10150
|
*/
|
|
10142
|
-
static async detectComments(content, client, instructions, tone, density, language, sourceLanguage, onActivity) {
|
|
10151
|
+
static async detectComments(content, client, instructions, tone, density, language, sourceLanguage, onActivity, onChunkResults) {
|
|
10143
10152
|
return detectInChunks(
|
|
10144
10153
|
client,
|
|
10145
10154
|
content,
|
|
@@ -10147,7 +10156,8 @@ var AnnotationDetection = class {
|
|
|
10147
10156
|
"comment",
|
|
10148
10157
|
COMMENT_ELEMENT_SCHEMA,
|
|
10149
10158
|
(items) => MotivationParsers.parseComments(items, content),
|
|
10150
|
-
onActivity
|
|
10159
|
+
onActivity,
|
|
10160
|
+
onChunkResults
|
|
10151
10161
|
);
|
|
10152
10162
|
}
|
|
10153
10163
|
/**
|
|
@@ -10157,7 +10167,7 @@ var AnnotationDetection = class {
|
|
|
10157
10167
|
* applies, used in the prompt so the LLM analyzes non-English source
|
|
10158
10168
|
* correctly.
|
|
10159
10169
|
*/
|
|
10160
|
-
static async detectHighlights(content, client, instructions, density, sourceLanguage, onActivity) {
|
|
10170
|
+
static async detectHighlights(content, client, instructions, density, sourceLanguage, onActivity, onChunkResults) {
|
|
10161
10171
|
return detectInChunks(
|
|
10162
10172
|
client,
|
|
10163
10173
|
content,
|
|
@@ -10165,7 +10175,8 @@ var AnnotationDetection = class {
|
|
|
10165
10175
|
"highlight",
|
|
10166
10176
|
HIGHLIGHT_ELEMENT_SCHEMA,
|
|
10167
10177
|
(items) => MotivationParsers.parseHighlights(items, content),
|
|
10168
|
-
onActivity
|
|
10178
|
+
onActivity,
|
|
10179
|
+
onChunkResults
|
|
10169
10180
|
);
|
|
10170
10181
|
}
|
|
10171
10182
|
/**
|
|
@@ -10175,7 +10186,7 @@ var AnnotationDetection = class {
|
|
|
10175
10186
|
* (annotation body locale). `sourceLanguage` is the locale of the content
|
|
10176
10187
|
* being analyzed (source-resource locale).
|
|
10177
10188
|
*/
|
|
10178
|
-
static async detectAssessments(content, client, instructions, tone, density, language, sourceLanguage, onActivity) {
|
|
10189
|
+
static async detectAssessments(content, client, instructions, tone, density, language, sourceLanguage, onActivity, onChunkResults) {
|
|
10179
10190
|
return detectInChunks(
|
|
10180
10191
|
client,
|
|
10181
10192
|
content,
|
|
@@ -10183,7 +10194,8 @@ var AnnotationDetection = class {
|
|
|
10183
10194
|
"assessment",
|
|
10184
10195
|
ASSESSMENT_ELEMENT_SCHEMA,
|
|
10185
10196
|
(items) => MotivationParsers.parseAssessments(items, content),
|
|
10186
|
-
onActivity
|
|
10197
|
+
onActivity,
|
|
10198
|
+
onChunkResults
|
|
10187
10199
|
);
|
|
10188
10200
|
}
|
|
10189
10201
|
/**
|
|
@@ -10198,7 +10210,7 @@ var AnnotationDetection = class {
|
|
|
10198
10210
|
* identifiers, not LLM-generated text — so it's consumed at the body-stamp
|
|
10199
10211
|
* site, not here.
|
|
10200
10212
|
*/
|
|
10201
|
-
static async detectTags(content, client, schema, category, sourceLanguage, onActivity) {
|
|
10213
|
+
static async detectTags(content, client, schema, category, sourceLanguage, onActivity, onChunkResults) {
|
|
10202
10214
|
const categoryInfo = schema.tags.find((t) => t.name === category);
|
|
10203
10215
|
if (!categoryInfo) {
|
|
10204
10216
|
throw new Error(`Invalid category "${category}" for schema ${schema.id}`);
|
|
@@ -10219,7 +10231,8 @@ var AnnotationDetection = class {
|
|
|
10219
10231
|
"tag",
|
|
10220
10232
|
TAG_ELEMENT_SCHEMA,
|
|
10221
10233
|
(items) => MotivationParsers.parseTags(items),
|
|
10222
|
-
onActivity
|
|
10234
|
+
onActivity,
|
|
10235
|
+
onChunkResults ? async (raw) => onChunkResults(MotivationParsers.validateTagOffsets(raw, content, category)) : void 0
|
|
10223
10236
|
);
|
|
10224
10237
|
return MotivationParsers.validateTagOffsets(parsedTags, content, category);
|
|
10225
10238
|
}
|
|
@@ -10256,20 +10269,22 @@ ${piece}
|
|
|
10256
10269
|
pieceChars: piece.length,
|
|
10257
10270
|
error: err instanceof Error ? err.message : String(err)
|
|
10258
10271
|
});
|
|
10259
|
-
return;
|
|
10272
|
+
return void 0;
|
|
10260
10273
|
}
|
|
10261
10274
|
if (counted === void 0) {
|
|
10262
10275
|
logger2.warn("Count-verifier answer carried no number \u2014 yield check skipped for this chunk", { pieceChars: piece.length });
|
|
10263
|
-
return;
|
|
10276
|
+
return void 0;
|
|
10264
10277
|
}
|
|
10265
10278
|
if (items.length * YIELD_COLLAPSE_BAND < counted) {
|
|
10266
10279
|
throw new YieldCollapseError(
|
|
10267
10280
|
`Extraction found ${items.length} entities where a count call reports ~${counted} mentions (band \xD7${YIELD_COLLAPSE_BAND}) on a ${piece.length}-char chunk \u2014 silent yield collapse (F7): deterministic \u2014 a same-size retry returns the identical under-report.`,
|
|
10268
|
-
[...items]
|
|
10281
|
+
[...items],
|
|
10282
|
+
{ found: items.length, counted, pieceChars: piece.length }
|
|
10269
10283
|
);
|
|
10270
10284
|
}
|
|
10285
|
+
return counted;
|
|
10271
10286
|
}
|
|
10272
|
-
async function extractEntities(exact, entityTypes, client, includeDescriptiveReferences, logger2, sourceLanguage, onActivity) {
|
|
10287
|
+
async function extractEntities(exact, entityTypes, client, includeDescriptiveReferences, logger2, sourceLanguage, onActivity, onUnderReport, onCounted, onChunkResults) {
|
|
10273
10288
|
const entityTypesDescription = entityTypes.map((et) => {
|
|
10274
10289
|
if (typeof et === "string") {
|
|
10275
10290
|
return et;
|
|
@@ -10350,14 +10365,17 @@ Example output:
|
|
|
10350
10365
|
items: response.items.length
|
|
10351
10366
|
});
|
|
10352
10367
|
assertNotTruncated(response, "Entity extraction", i + 1, chunks.length, outputBudget);
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10368
|
+
const counted = verifyYield ? await assertYieldNotCollapsed(client, piece, response.items, entityTypesDescription, logger2) : void 0;
|
|
10369
|
+
return {
|
|
10370
|
+
items: response.items,
|
|
10371
|
+
...response.usage ? { usage: response.usage } : {},
|
|
10372
|
+
...counted !== void 0 ? { counted } : {}
|
|
10373
|
+
};
|
|
10374
|
+
}, logger2, onUnderReport, onCounted);
|
|
10375
|
+
const fromChunk = [];
|
|
10358
10376
|
for (const e of items) {
|
|
10359
10377
|
if (isObject(e) && isString(e.exact) && isString(e.entityType)) {
|
|
10360
|
-
|
|
10378
|
+
fromChunk.push({
|
|
10361
10379
|
exact: e.exact,
|
|
10362
10380
|
entityType: e.entityType,
|
|
10363
10381
|
...isString(e.prefix) ? { prefix: e.prefix } : {},
|
|
@@ -10367,6 +10385,8 @@ Example output:
|
|
|
10367
10385
|
logger2.debug("Dropped malformed LLM entity", { entity: e });
|
|
10368
10386
|
}
|
|
10369
10387
|
}
|
|
10388
|
+
collected.push(...fromChunk);
|
|
10389
|
+
await onChunkResults?.(fromChunk);
|
|
10370
10390
|
if (i < chunks.length - 1) {
|
|
10371
10391
|
onActivity?.(i + 1, chunks.length);
|
|
10372
10392
|
}
|
|
@@ -10725,16 +10745,18 @@ function annotationDedupeKey(ann) {
|
|
|
10725
10745
|
}
|
|
10726
10746
|
return [ann.motivation, anchor, JSON.stringify(ann.body ?? null)].join("|");
|
|
10727
10747
|
}
|
|
10728
|
-
function
|
|
10748
|
+
function makeSpanDeduper() {
|
|
10729
10749
|
const seen = /* @__PURE__ */ new Set();
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
const
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
|
|
10750
|
+
return (annotations) => {
|
|
10751
|
+
const out = [];
|
|
10752
|
+
for (const ann of annotations) {
|
|
10753
|
+
const key = annotationDedupeKey(ann);
|
|
10754
|
+
if (seen.has(key)) continue;
|
|
10755
|
+
seen.add(key);
|
|
10756
|
+
out.push(ann);
|
|
10757
|
+
}
|
|
10758
|
+
return out;
|
|
10759
|
+
};
|
|
10738
10760
|
}
|
|
10739
10761
|
function buildTextAnnotation(content, resourceId, userId, generator, motivation, match, body) {
|
|
10740
10762
|
if (content.substring(match.start, match.end) !== match.exact) {
|
|
@@ -10792,7 +10814,12 @@ function buildPdfAnnotation(anchored, resourceId, userId, generator, motivation,
|
|
|
10792
10814
|
Math.max(...overlap.map((i) => i.end))
|
|
10793
10815
|
) : "";
|
|
10794
10816
|
const normalize = (s) => s.replace(/\s+/g, " ").trim();
|
|
10795
|
-
if (rects.length === 0
|
|
10817
|
+
if (rects.length === 0) {
|
|
10818
|
+
throw new Error(
|
|
10819
|
+
`buildPdfAnnotation invariant: no rects located for offsets ${match.start}-${match.end} for resource ${resourceId}, motivation ${motivation}`
|
|
10820
|
+
);
|
|
10821
|
+
}
|
|
10822
|
+
if (!normalize(coveredText).includes(normalize(match.exact))) {
|
|
10796
10823
|
throw new Error(
|
|
10797
10824
|
`buildPdfAnnotation invariant: covered text does not contain exact for resource ${resourceId}, motivation ${motivation}`
|
|
10798
10825
|
);
|
|
@@ -10828,27 +10855,32 @@ function buildPdfAnnotation(anchored, resourceId, userId, generator, motivation,
|
|
|
10828
10855
|
...body !== void 0 ? { body } : {}
|
|
10829
10856
|
};
|
|
10830
10857
|
}
|
|
10831
|
-
async function processHighlightJob(content, inferenceClient, params, buildAnnotation, onProgress) {
|
|
10858
|
+
async function processHighlightJob(content, inferenceClient, params, buildAnnotation, onProgress, onChunkComplete) {
|
|
10832
10859
|
const echo = detectionEcho(params);
|
|
10833
10860
|
onProgress(10, { code: "loading" }, echo);
|
|
10834
10861
|
onProgress(30, { code: "analyzing" }, echo);
|
|
10835
|
-
const
|
|
10862
|
+
const dedupe = makeSpanDeduper();
|
|
10863
|
+
let found = 0;
|
|
10864
|
+
let created = 0;
|
|
10865
|
+
await AnnotationDetection.detectHighlights(
|
|
10836
10866
|
content,
|
|
10837
10867
|
inferenceClient,
|
|
10838
10868
|
params.instructions,
|
|
10839
10869
|
params.density,
|
|
10840
10870
|
params.sourceLanguage,
|
|
10841
10871
|
// Liveness (chunk boundaries + in-flight heartbeat): 30–60 band.
|
|
10842
|
-
(completed, total) => onProgress(30 + Math.round(completed / total * 30), { code: "analyzing" }, echo)
|
|
10872
|
+
(completed, total) => onProgress(30 + Math.round(completed / total * 30), { code: "analyzing" }, echo),
|
|
10873
|
+
async (matches) => {
|
|
10874
|
+
found += matches.length;
|
|
10875
|
+
const fresh = dedupe(matches.map((h) => buildAnnotation("highlighting", h)));
|
|
10876
|
+
created += fresh.length;
|
|
10877
|
+
onProgress(60, { code: "creating-annotations", count: created }, echo);
|
|
10878
|
+
await onChunkComplete(fresh);
|
|
10879
|
+
}
|
|
10843
10880
|
);
|
|
10844
|
-
onProgress(
|
|
10845
|
-
const annotations = dedupeAnnotations(highlights.map(
|
|
10846
|
-
(h) => buildAnnotation("highlighting", h)
|
|
10847
|
-
));
|
|
10848
|
-
onProgress(100, { code: "complete-created", count: annotations.length, kind: "highlight" }, echo);
|
|
10881
|
+
onProgress(100, { code: "complete-created", count: created, kind: "highlight" }, echo);
|
|
10849
10882
|
return {
|
|
10850
|
-
|
|
10851
|
-
result: { kind: "highlight-annotation", highlightsFound: highlights.length, highlightsCreated: annotations.length }
|
|
10883
|
+
result: { kind: "highlight-annotation", highlightsFound: found, highlightsCreated: created }
|
|
10852
10884
|
};
|
|
10853
10885
|
}
|
|
10854
10886
|
function detectionEcho(p) {
|
|
@@ -10858,11 +10890,15 @@ function detectionEcho(p) {
|
|
|
10858
10890
|
if (p.density !== void 0) requestParams.push({ label: "density", value: String(p.density) });
|
|
10859
10891
|
return requestParams.length > 0 ? { requestParams } : {};
|
|
10860
10892
|
}
|
|
10861
|
-
async function processCommentJob(content, inferenceClient, params, buildAnnotation, onProgress) {
|
|
10893
|
+
async function processCommentJob(content, inferenceClient, params, buildAnnotation, onProgress, onChunkComplete) {
|
|
10862
10894
|
const echo = detectionEcho(params);
|
|
10863
10895
|
onProgress(10, { code: "loading" }, echo);
|
|
10864
10896
|
onProgress(30, { code: "analyzing" }, echo);
|
|
10865
|
-
const
|
|
10897
|
+
const bodyLanguage = params.language ?? "en";
|
|
10898
|
+
const dedupe = makeSpanDeduper();
|
|
10899
|
+
let found = 0;
|
|
10900
|
+
let created = 0;
|
|
10901
|
+
await AnnotationDetection.detectComments(
|
|
10866
10902
|
content,
|
|
10867
10903
|
inferenceClient,
|
|
10868
10904
|
params.instructions,
|
|
@@ -10871,31 +10907,38 @@ async function processCommentJob(content, inferenceClient, params, buildAnnotati
|
|
|
10871
10907
|
params.language,
|
|
10872
10908
|
params.sourceLanguage,
|
|
10873
10909
|
// Liveness (chunk boundaries + in-flight heartbeat): 30–60 band.
|
|
10874
|
-
(completed, total) => onProgress(30 + Math.round(completed / total * 30), { code: "analyzing" }, echo)
|
|
10910
|
+
(completed, total) => onProgress(30 + Math.round(completed / total * 30), { code: "analyzing" }, echo),
|
|
10911
|
+
async (comments) => {
|
|
10912
|
+
found += comments.length;
|
|
10913
|
+
const fresh = dedupe(comments.map(
|
|
10914
|
+
(c) => (
|
|
10915
|
+
// Match the pre-#651 CommentAnnotationWorker: include format and
|
|
10916
|
+
// language on the body TextualBody. Optional in the schema, but
|
|
10917
|
+
// consumers that do language-aware rendering rely on them.
|
|
10918
|
+
buildAnnotation("commenting", c, [
|
|
10919
|
+
{ type: "TextualBody", value: c.comment, purpose: "commenting", format: "text/plain", language: bodyLanguage }
|
|
10920
|
+
])
|
|
10921
|
+
)
|
|
10922
|
+
));
|
|
10923
|
+
created += fresh.length;
|
|
10924
|
+
onProgress(60, { code: "creating-annotations", count: created }, echo);
|
|
10925
|
+
await onChunkComplete(fresh);
|
|
10926
|
+
}
|
|
10875
10927
|
);
|
|
10876
|
-
onProgress(
|
|
10877
|
-
const bodyLanguage = params.language ?? "en";
|
|
10878
|
-
const annotations = dedupeAnnotations(comments.map(
|
|
10879
|
-
(c) => (
|
|
10880
|
-
// Match the pre-#651 CommentAnnotationWorker: include format and
|
|
10881
|
-
// language on the body TextualBody. Optional in the schema, but
|
|
10882
|
-
// consumers that do language-aware rendering rely on them.
|
|
10883
|
-
buildAnnotation("commenting", c, [
|
|
10884
|
-
{ type: "TextualBody", value: c.comment, purpose: "commenting", format: "text/plain", language: bodyLanguage }
|
|
10885
|
-
])
|
|
10886
|
-
)
|
|
10887
|
-
));
|
|
10888
|
-
onProgress(100, { code: "complete-created", count: annotations.length, kind: "comment" }, echo);
|
|
10928
|
+
onProgress(100, { code: "complete-created", count: created, kind: "comment" }, echo);
|
|
10889
10929
|
return {
|
|
10890
|
-
|
|
10891
|
-
result: { kind: "comment-annotation", commentsFound: comments.length, commentsCreated: annotations.length }
|
|
10930
|
+
result: { kind: "comment-annotation", commentsFound: found, commentsCreated: created }
|
|
10892
10931
|
};
|
|
10893
10932
|
}
|
|
10894
|
-
async function processAssessmentJob(content, inferenceClient, params, buildAnnotation, onProgress) {
|
|
10933
|
+
async function processAssessmentJob(content, inferenceClient, params, buildAnnotation, onProgress, onChunkComplete) {
|
|
10895
10934
|
const echo = detectionEcho(params);
|
|
10896
10935
|
onProgress(10, { code: "loading" }, echo);
|
|
10897
10936
|
onProgress(30, { code: "analyzing" }, echo);
|
|
10898
|
-
const
|
|
10937
|
+
const bodyLanguage = params.language ?? "en";
|
|
10938
|
+
const dedupe = makeSpanDeduper();
|
|
10939
|
+
let found = 0;
|
|
10940
|
+
let created = 0;
|
|
10941
|
+
await AnnotationDetection.detectAssessments(
|
|
10899
10942
|
content,
|
|
10900
10943
|
inferenceClient,
|
|
10901
10944
|
params.instructions,
|
|
@@ -10904,40 +10947,45 @@ async function processAssessmentJob(content, inferenceClient, params, buildAnnot
|
|
|
10904
10947
|
params.language,
|
|
10905
10948
|
params.sourceLanguage,
|
|
10906
10949
|
// Liveness (chunk boundaries + in-flight heartbeat): 30–60 band.
|
|
10907
|
-
(completed, total) => onProgress(30 + Math.round(completed / total * 30), { code: "analyzing" }, echo)
|
|
10950
|
+
(completed, total) => onProgress(30 + Math.round(completed / total * 30), { code: "analyzing" }, echo),
|
|
10951
|
+
async (assessments) => {
|
|
10952
|
+
found += assessments.length;
|
|
10953
|
+
const fresh = dedupe(assessments.map(
|
|
10954
|
+
(a) => (
|
|
10955
|
+
// Single-object body with purpose aligned to motivation, matching the
|
|
10956
|
+
// pre-#651 AssessmentAnnotationWorker's shape and the majority of
|
|
10957
|
+
// persisted assessments. Do not switch to an array or to
|
|
10958
|
+
// purpose='describing' — that loses the "this is an assessment, not
|
|
10959
|
+
// a description" signal and breaks existing readers that access
|
|
10960
|
+
// `body.value` directly on the object.
|
|
10961
|
+
buildAnnotation("assessing", a, {
|
|
10962
|
+
type: "TextualBody",
|
|
10963
|
+
value: a.assessment,
|
|
10964
|
+
purpose: "assessing",
|
|
10965
|
+
format: "text/plain",
|
|
10966
|
+
language: bodyLanguage
|
|
10967
|
+
})
|
|
10968
|
+
)
|
|
10969
|
+
));
|
|
10970
|
+
created += fresh.length;
|
|
10971
|
+
onProgress(60, { code: "creating-annotations", count: created }, echo);
|
|
10972
|
+
await onChunkComplete(fresh);
|
|
10973
|
+
}
|
|
10908
10974
|
);
|
|
10909
|
-
onProgress(
|
|
10910
|
-
const bodyLanguage = params.language ?? "en";
|
|
10911
|
-
const annotations = dedupeAnnotations(assessments.map(
|
|
10912
|
-
(a) => (
|
|
10913
|
-
// Single-object body with purpose aligned to motivation, matching the
|
|
10914
|
-
// pre-#651 AssessmentAnnotationWorker's shape and the majority of
|
|
10915
|
-
// persisted assessments. Do not switch to an array or to
|
|
10916
|
-
// purpose='describing' — that loses the "this is an assessment, not
|
|
10917
|
-
// a description" signal and breaks existing readers that access
|
|
10918
|
-
// `body.value` directly on the object.
|
|
10919
|
-
buildAnnotation("assessing", a, {
|
|
10920
|
-
type: "TextualBody",
|
|
10921
|
-
value: a.assessment,
|
|
10922
|
-
purpose: "assessing",
|
|
10923
|
-
format: "text/plain",
|
|
10924
|
-
language: bodyLanguage
|
|
10925
|
-
})
|
|
10926
|
-
)
|
|
10927
|
-
));
|
|
10928
|
-
onProgress(100, { code: "complete-created", count: annotations.length, kind: "assessment" }, echo);
|
|
10975
|
+
onProgress(100, { code: "complete-created", count: created, kind: "assessment" }, echo);
|
|
10929
10976
|
return {
|
|
10930
|
-
|
|
10931
|
-
result: { kind: "assessment-annotation", assessmentsFound: assessments.length, assessmentsCreated: annotations.length }
|
|
10977
|
+
result: { kind: "assessment-annotation", assessmentsFound: found, assessmentsCreated: created }
|
|
10932
10978
|
};
|
|
10933
10979
|
}
|
|
10934
|
-
async function processReferenceJob(content, inferenceClient, params, buildAnnotation, onProgress, logger2, onUnitComplete, signal) {
|
|
10980
|
+
async function processReferenceJob(content, inferenceClient, params, buildAnnotation, onProgress, logger2, onUnitComplete, signal, onChunkComplete) {
|
|
10935
10981
|
const entityTypeNames = params.entityTypes.map(String);
|
|
10936
10982
|
const requestParams = [{ label: "entity-types", value: entityTypeNames.join(", ") }];
|
|
10937
10983
|
const completedItems = [];
|
|
10938
10984
|
let totalFound = 0;
|
|
10939
10985
|
let totalEmitted = 0;
|
|
10940
10986
|
let errors = 0;
|
|
10987
|
+
let totalUnderReportedPieces = 0;
|
|
10988
|
+
let totalExpected = 0;
|
|
10941
10989
|
onProgress(10, { code: "loading" }, { requestParams });
|
|
10942
10990
|
const bodyLanguage = params.language ?? "en";
|
|
10943
10991
|
let completed = 0;
|
|
@@ -10949,6 +10997,7 @@ async function processReferenceJob(content, inferenceClient, params, buildAnnota
|
|
|
10949
10997
|
processed: completed,
|
|
10950
10998
|
total,
|
|
10951
10999
|
entitiesFound: totalFound,
|
|
11000
|
+
...totalExpected > 0 ? { entitiesExpected: totalExpected } : {},
|
|
10952
11001
|
entitiesEmitted: totalEmitted,
|
|
10953
11002
|
completedItems: [...completedItems],
|
|
10954
11003
|
requestParams
|
|
@@ -10958,7 +11007,14 @@ async function processReferenceJob(content, inferenceClient, params, buildAnnota
|
|
|
10958
11007
|
if (!entityTypeName) return;
|
|
10959
11008
|
if (signal?.aborted) return;
|
|
10960
11009
|
emitTypeProgress(entityTypeName);
|
|
10961
|
-
const
|
|
11010
|
+
const unresolvedBody = [
|
|
11011
|
+
{ type: "TextualBody", value: entityTypeName, purpose: "tagging", format: "text/plain", language: bodyLanguage }
|
|
11012
|
+
];
|
|
11013
|
+
const dedupe = makeSpanDeduper();
|
|
11014
|
+
let unitFound = 0;
|
|
11015
|
+
let unitPersisted = 0;
|
|
11016
|
+
let underReported;
|
|
11017
|
+
await extractEntities(
|
|
10962
11018
|
content,
|
|
10963
11019
|
[entityTypeName],
|
|
10964
11020
|
inferenceClient,
|
|
@@ -10970,54 +11026,80 @@ async function processReferenceJob(content, inferenceClient, params, buildAnnota
|
|
|
10970
11026
|
// not silent. It repeats the current position rather than inventing an
|
|
10971
11027
|
// advance — the stall watchdog, janitor and client timeout need a signal,
|
|
10972
11028
|
// not a monotone.
|
|
10973
|
-
() => emitTypeProgress(entityTypeName)
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10981
|
-
|
|
10982
|
-
|
|
10983
|
-
|
|
10984
|
-
}
|
|
10985
|
-
|
|
10986
|
-
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
11029
|
+
() => emitTypeProgress(entityTypeName),
|
|
11030
|
+
(verdict) => {
|
|
11031
|
+
underReported = {
|
|
11032
|
+
pieces: (underReported?.pieces ?? 0) + 1,
|
|
11033
|
+
found: (underReported?.found ?? 0) + verdict.found,
|
|
11034
|
+
counted: (underReported?.counted ?? 0) + verdict.counted
|
|
11035
|
+
};
|
|
11036
|
+
},
|
|
11037
|
+
(counted) => {
|
|
11038
|
+
totalExpected += counted;
|
|
11039
|
+
emitTypeProgress(entityTypeName);
|
|
11040
|
+
},
|
|
11041
|
+
async (chunkEntities) => {
|
|
11042
|
+
const built = [];
|
|
11043
|
+
for (const entity of chunkEntities) {
|
|
11044
|
+
const reconciled = reconcileSelector(content, {
|
|
11045
|
+
exact: entity.exact,
|
|
11046
|
+
...entity.prefix !== void 0 ? { prefix: entity.prefix } : {},
|
|
11047
|
+
...entity.suffix !== void 0 ? { suffix: entity.suffix } : {}
|
|
11048
|
+
});
|
|
11049
|
+
if (!reconciled) {
|
|
11050
|
+
logger2.error("Entity dropped \u2014 text not found in source", {
|
|
11051
|
+
text: entity.exact,
|
|
11052
|
+
entityType: entity.entityType
|
|
11053
|
+
});
|
|
11054
|
+
errors++;
|
|
11055
|
+
continue;
|
|
11056
|
+
}
|
|
11057
|
+
noteAnchor("reference", entity.exact, reconciled.anchorMethod, logger2);
|
|
11058
|
+
built.push(buildAnnotation("linking", toMatch(reconciled), unresolvedBody));
|
|
11059
|
+
}
|
|
11060
|
+
const fresh = dedupe(built);
|
|
11061
|
+
await onChunkComplete?.(fresh);
|
|
11062
|
+
unitFound += chunkEntities.length;
|
|
11063
|
+
unitPersisted += fresh.length;
|
|
11064
|
+
totalFound += chunkEntities.length;
|
|
11065
|
+
totalEmitted += fresh.length;
|
|
11066
|
+
emitTypeProgress(entityTypeName);
|
|
10992
11067
|
}
|
|
10993
|
-
|
|
10994
|
-
|
|
10995
|
-
built.push(ann);
|
|
10996
|
-
}
|
|
10997
|
-
const unitAnnotations = dedupeAnnotations(built);
|
|
10998
|
-
await onUnitComplete(entityTypeName, unitAnnotations);
|
|
10999
|
-
totalEmitted += unitAnnotations.length;
|
|
11000
|
-
totalFound += extractedEntities.length;
|
|
11068
|
+
);
|
|
11069
|
+
await onUnitComplete(entityTypeName);
|
|
11001
11070
|
completedItems.push({
|
|
11002
11071
|
value: entityTypeName,
|
|
11003
|
-
foundCount:
|
|
11004
|
-
persistedCount:
|
|
11072
|
+
foundCount: unitFound,
|
|
11073
|
+
persistedCount: unitPersisted,
|
|
11074
|
+
...underReported ? { underReported } : {}
|
|
11005
11075
|
});
|
|
11076
|
+
if (underReported) totalUnderReportedPieces += underReported.pieces;
|
|
11006
11077
|
completed++;
|
|
11007
11078
|
emitTypeProgress(entityTypeName);
|
|
11008
11079
|
});
|
|
11009
11080
|
onProgress(100, { code: "complete-created", count: totalEmitted, kind: "reference" }, {
|
|
11081
|
+
...totalExpected > 0 ? { entitiesExpected: totalExpected } : {},
|
|
11010
11082
|
completedItems: [...completedItems],
|
|
11011
11083
|
requestParams
|
|
11012
11084
|
});
|
|
11013
11085
|
return {
|
|
11014
|
-
result: {
|
|
11086
|
+
result: {
|
|
11087
|
+
kind: "reference-annotation",
|
|
11088
|
+
totalFound,
|
|
11089
|
+
totalEmitted,
|
|
11090
|
+
errors,
|
|
11091
|
+
...totalUnderReportedPieces > 0 ? { underReportedPieces: totalUnderReportedPieces } : {}
|
|
11092
|
+
}
|
|
11015
11093
|
};
|
|
11016
11094
|
}
|
|
11017
|
-
async function processTagJob(content, inferenceClient, params, buildAnnotation, onProgress) {
|
|
11095
|
+
async function processTagJob(content, inferenceClient, params, buildAnnotation, onProgress, onChunkComplete) {
|
|
11018
11096
|
onProgress(10, { code: "loading" });
|
|
11019
11097
|
onProgress(30, { code: "analyzing-tags" });
|
|
11020
|
-
const
|
|
11098
|
+
const bodyLanguage = params.language ?? "en";
|
|
11099
|
+
const dedupe = makeSpanDeduper();
|
|
11100
|
+
let found = 0;
|
|
11101
|
+
let created = 0;
|
|
11102
|
+
const byCategory = {};
|
|
11021
11103
|
const completedItems = [];
|
|
11022
11104
|
for (let c = 0; c < params.categories.length; c++) {
|
|
11023
11105
|
const category = params.categories[c];
|
|
@@ -11032,7 +11114,8 @@ async function processTagJob(content, inferenceClient, params, buildAnnotation,
|
|
|
11032
11114
|
{ code: "analyzing-tags" },
|
|
11033
11115
|
position()
|
|
11034
11116
|
);
|
|
11035
|
-
|
|
11117
|
+
let categoryFound = 0;
|
|
11118
|
+
await AnnotationDetection.detectTags(
|
|
11036
11119
|
content,
|
|
11037
11120
|
inferenceClient,
|
|
11038
11121
|
params.schema,
|
|
@@ -11044,31 +11127,32 @@ async function processTagJob(content, inferenceClient, params, buildAnnotation,
|
|
|
11044
11127
|
30 + Math.round((c + completed / total) / params.categories.length * 30),
|
|
11045
11128
|
{ code: "analyzing-tags" },
|
|
11046
11129
|
position()
|
|
11047
|
-
)
|
|
11130
|
+
),
|
|
11131
|
+
async (matches) => {
|
|
11132
|
+
categoryFound += matches.length;
|
|
11133
|
+
const fresh = dedupe(matches.map((t) => {
|
|
11134
|
+
const cat = t.category ?? "unknown";
|
|
11135
|
+
return buildAnnotation("tagging", t, [
|
|
11136
|
+
{ type: "TextualBody", value: cat, purpose: "tagging", format: "text/plain", language: bodyLanguage },
|
|
11137
|
+
{ type: "TextualBody", value: params.schema.id, purpose: "classifying", format: "text/plain" }
|
|
11138
|
+
]);
|
|
11139
|
+
}));
|
|
11140
|
+
created += fresh.length;
|
|
11141
|
+
for (const ann of fresh) {
|
|
11142
|
+
const body = ann.body;
|
|
11143
|
+
const cat = Array.isArray(body) && typeof body[0]?.value === "string" ? body[0].value : "unknown";
|
|
11144
|
+
byCategory[cat] = (byCategory[cat] ?? 0) + 1;
|
|
11145
|
+
}
|
|
11146
|
+
onProgress(60, { code: "creating-tag-annotations", count: created });
|
|
11147
|
+
await onChunkComplete(fresh);
|
|
11148
|
+
}
|
|
11048
11149
|
);
|
|
11049
|
-
|
|
11050
|
-
|
|
11051
|
-
}
|
|
11052
|
-
const tags = allTags;
|
|
11053
|
-
onProgress(60, { code: "creating-tag-annotations", count: tags.length });
|
|
11054
|
-
const bodyLanguage = params.language ?? "en";
|
|
11055
|
-
const annotations = dedupeAnnotations(tags.map((t) => {
|
|
11056
|
-
const category = t.category ?? "unknown";
|
|
11057
|
-
return buildAnnotation("tagging", t, [
|
|
11058
|
-
{ type: "TextualBody", value: category, purpose: "tagging", format: "text/plain", language: bodyLanguage },
|
|
11059
|
-
{ type: "TextualBody", value: params.schema.id, purpose: "classifying", format: "text/plain" }
|
|
11060
|
-
]);
|
|
11061
|
-
}));
|
|
11062
|
-
const byCategory = {};
|
|
11063
|
-
for (const ann of annotations) {
|
|
11064
|
-
const body = ann.body;
|
|
11065
|
-
const category = Array.isArray(body) && typeof body[0]?.value === "string" ? body[0].value : "unknown";
|
|
11066
|
-
byCategory[category] = (byCategory[category] ?? 0) + 1;
|
|
11150
|
+
found += categoryFound;
|
|
11151
|
+
completedItems.push({ value: category, foundCount: categoryFound });
|
|
11067
11152
|
}
|
|
11068
|
-
onProgress(100, { code: "complete-created", count:
|
|
11153
|
+
onProgress(100, { code: "complete-created", count: created, kind: "tag" });
|
|
11069
11154
|
return {
|
|
11070
|
-
|
|
11071
|
-
result: { kind: "tag-annotation", tagsFound: tags.length, tagsCreated: annotations.length, byCategory }
|
|
11155
|
+
result: { kind: "tag-annotation", tagsFound: found, tagsCreated: created, byCategory }
|
|
11072
11156
|
};
|
|
11073
11157
|
}
|
|
11074
11158
|
function assertWithinOutputBudget(byteLength) {
|
|
@@ -11269,13 +11353,40 @@ function referenceIdOf(job) {
|
|
|
11269
11353
|
}
|
|
11270
11354
|
var MARK_COMMIT_TIMEOUT_MS = 6e4;
|
|
11271
11355
|
async function commitAnnotations(session, resourceId, annotations) {
|
|
11272
|
-
if (annotations.length === 0) return;
|
|
11273
|
-
|
|
11274
|
-
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11356
|
+
if (annotations.length === 0) return void 0;
|
|
11357
|
+
try {
|
|
11358
|
+
await busRequest(
|
|
11359
|
+
workerBusAsPrimitive(session.client.transport.actor),
|
|
11360
|
+
"mark:commit",
|
|
11361
|
+
{ resourceId, annotations },
|
|
11362
|
+
MARK_COMMIT_TIMEOUT_MS
|
|
11363
|
+
);
|
|
11364
|
+
return "acknowledged";
|
|
11365
|
+
} catch (error) {
|
|
11366
|
+
if (!(error instanceof BusRequestError) || error.code !== "bus.timeout") throw error;
|
|
11367
|
+
const evidence = await probeDurability(session, resourceId, annotations);
|
|
11368
|
+
if (evidence === "probe-confirmed") return evidence;
|
|
11369
|
+
throw new CommitDurabilityError(error.message, evidence, error);
|
|
11370
|
+
}
|
|
11371
|
+
}
|
|
11372
|
+
var CommitDurabilityError = class extends Error {
|
|
11373
|
+
constructor(message, durability, cause) {
|
|
11374
|
+
super(message, { cause });
|
|
11375
|
+
this.durability = durability;
|
|
11376
|
+
}
|
|
11377
|
+
durability;
|
|
11378
|
+
name = "CommitDurabilityError";
|
|
11379
|
+
};
|
|
11380
|
+
async function probeDurability(session, resourceId$1, annotations) {
|
|
11381
|
+
const last = annotations[annotations.length - 1];
|
|
11382
|
+
if (!last) return "probe-unreachable";
|
|
11383
|
+
try {
|
|
11384
|
+
await session.client.browse.annotation(resourceId(resourceId$1), annotationId(String(last.id))).fresh();
|
|
11385
|
+
return "probe-confirmed";
|
|
11386
|
+
} catch (error) {
|
|
11387
|
+
const code = isObject(error) && isString(error.code) ? error.code : void 0;
|
|
11388
|
+
return code === "bus.rejected" ? "probe-refused" : "probe-unreachable";
|
|
11389
|
+
}
|
|
11279
11390
|
}
|
|
11280
11391
|
async function emitEvent(session, channel, payload) {
|
|
11281
11392
|
await session.client.transport.emit(channel, payload);
|
|
@@ -11320,6 +11431,10 @@ function startWorkerProcess(config) {
|
|
|
11320
11431
|
error: message,
|
|
11321
11432
|
...completedUnits && completedUnits.length > 0 ? { completedUnits } : {},
|
|
11322
11433
|
...failureClass !== void 0 ? { failureClass } : {},
|
|
11434
|
+
// What the commit path OBSERVED about durability, when the failure
|
|
11435
|
+
// came from a commit at all. Present only on that path: absent means
|
|
11436
|
+
// the question never arose, never that durability was ruled out.
|
|
11437
|
+
...error instanceof CommitDurabilityError ? { durability: error.durability } : {},
|
|
11323
11438
|
// Whether this failure is the END, answered by the same predicate
|
|
11324
11439
|
// the queue applies at failJob (JOB-RESTART-SAFETY P5). Without
|
|
11325
11440
|
// it a client cannot tell a recovering run from a dead one: it
|
|
@@ -11376,6 +11491,12 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal)
|
|
|
11376
11491
|
jobType,
|
|
11377
11492
|
...annotationId ? { annotationId } : {}
|
|
11378
11493
|
};
|
|
11494
|
+
let durability;
|
|
11495
|
+
const record = (evidence) => {
|
|
11496
|
+
if (evidence === void 0) return;
|
|
11497
|
+
if (durability === void 0 || durability === "acknowledged") durability = evidence;
|
|
11498
|
+
};
|
|
11499
|
+
const terminalBase = () => ({ ...lifecycleBase, ...durability ? { durability } : {} });
|
|
11379
11500
|
await emitEvent(session, "job:start", lifecycleBase);
|
|
11380
11501
|
if (!config.jobTypes.includes(jobType)) {
|
|
11381
11502
|
adapter.failJob(jobId, `Worker not configured for job type: ${jobType}`);
|
|
@@ -11401,7 +11522,7 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal)
|
|
|
11401
11522
|
throw new DeterministicJobError(`Cannot run ${jobType} on resource ${resourceId$1}: anchored-text consult returned '${source.declined}'`);
|
|
11402
11523
|
}
|
|
11403
11524
|
await emitEvent(session, "job:complete", {
|
|
11404
|
-
...
|
|
11525
|
+
...terminalBase(),
|
|
11405
11526
|
result: {
|
|
11406
11527
|
kind: "declined",
|
|
11407
11528
|
declined: true,
|
|
@@ -11416,7 +11537,7 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal)
|
|
|
11416
11537
|
const onProgress = (percentage, message, extra) => {
|
|
11417
11538
|
adapter.touchActivity();
|
|
11418
11539
|
emitEvent(session, "job:report-progress", {
|
|
11419
|
-
...
|
|
11540
|
+
...terminalBase(),
|
|
11420
11541
|
percentage,
|
|
11421
11542
|
progress: {
|
|
11422
11543
|
percentage,
|
|
@@ -11428,44 +11549,56 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal)
|
|
|
11428
11549
|
});
|
|
11429
11550
|
};
|
|
11430
11551
|
if (jobType === "highlight-annotation") {
|
|
11431
|
-
const {
|
|
11552
|
+
const { result } = await processHighlightJob(
|
|
11432
11553
|
ready.text,
|
|
11433
11554
|
inferenceClient,
|
|
11434
11555
|
asJobParams(job.params),
|
|
11435
11556
|
ready.buildAnnotation,
|
|
11436
|
-
onProgress
|
|
11557
|
+
onProgress,
|
|
11558
|
+
// The durability write, per chunk, awaited; folds into the terminal
|
|
11559
|
+
// durability evidence like every commit.
|
|
11560
|
+
async (annotations) => {
|
|
11561
|
+
record(await commitAnnotations(session, String(resourceId$1), annotations));
|
|
11562
|
+
}
|
|
11437
11563
|
);
|
|
11438
|
-
await commitAnnotations(session, String(resourceId$1), annotations);
|
|
11439
11564
|
await emitEvent(session, "job:complete", {
|
|
11440
|
-
...
|
|
11565
|
+
...terminalBase(),
|
|
11441
11566
|
result
|
|
11442
11567
|
});
|
|
11443
11568
|
adapter.completeJob();
|
|
11444
11569
|
} else if (jobType === "comment-annotation") {
|
|
11445
|
-
const {
|
|
11570
|
+
const { result } = await processCommentJob(
|
|
11446
11571
|
ready.text,
|
|
11447
11572
|
inferenceClient,
|
|
11448
11573
|
asJobParams(job.params),
|
|
11449
11574
|
ready.buildAnnotation,
|
|
11450
|
-
onProgress
|
|
11575
|
+
onProgress,
|
|
11576
|
+
// The durability write, per chunk, awaited; folds into the terminal
|
|
11577
|
+
// durability evidence like every commit.
|
|
11578
|
+
async (annotations) => {
|
|
11579
|
+
record(await commitAnnotations(session, String(resourceId$1), annotations));
|
|
11580
|
+
}
|
|
11451
11581
|
);
|
|
11452
|
-
await commitAnnotations(session, String(resourceId$1), annotations);
|
|
11453
11582
|
await emitEvent(session, "job:complete", {
|
|
11454
|
-
...
|
|
11583
|
+
...terminalBase(),
|
|
11455
11584
|
result
|
|
11456
11585
|
});
|
|
11457
11586
|
adapter.completeJob();
|
|
11458
11587
|
} else if (jobType === "assessment-annotation") {
|
|
11459
|
-
const {
|
|
11588
|
+
const { result } = await processAssessmentJob(
|
|
11460
11589
|
ready.text,
|
|
11461
11590
|
inferenceClient,
|
|
11462
11591
|
asJobParams(job.params),
|
|
11463
11592
|
ready.buildAnnotation,
|
|
11464
|
-
onProgress
|
|
11593
|
+
onProgress,
|
|
11594
|
+
// The durability write, per chunk, awaited; folds into the terminal
|
|
11595
|
+
// durability evidence like every commit.
|
|
11596
|
+
async (annotations) => {
|
|
11597
|
+
record(await commitAnnotations(session, String(resourceId$1), annotations));
|
|
11598
|
+
}
|
|
11465
11599
|
);
|
|
11466
|
-
await commitAnnotations(session, String(resourceId$1), annotations);
|
|
11467
11600
|
await emitEvent(session, "job:complete", {
|
|
11468
|
-
...
|
|
11601
|
+
...terminalBase(),
|
|
11469
11602
|
result
|
|
11470
11603
|
});
|
|
11471
11604
|
adapter.completeJob();
|
|
@@ -11485,40 +11618,48 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal)
|
|
|
11485
11618
|
ready.buildAnnotation,
|
|
11486
11619
|
onProgress,
|
|
11487
11620
|
config.logger,
|
|
11488
|
-
async (unit
|
|
11489
|
-
await commitAnnotations(session, String(resourceId$1), annotations);
|
|
11621
|
+
async (unit) => {
|
|
11490
11622
|
committed.push(unit);
|
|
11491
11623
|
await emitEvent(session, "job:checkpoint", {
|
|
11492
11624
|
jobId: job.jobId,
|
|
11493
11625
|
completedUnits: [...committed]
|
|
11494
11626
|
});
|
|
11495
11627
|
},
|
|
11496
|
-
signal
|
|
11628
|
+
signal,
|
|
11629
|
+
// The durability write, per chunk, awaited; folds into the terminal
|
|
11630
|
+
// durability evidence like every commit.
|
|
11631
|
+
async (annotations) => {
|
|
11632
|
+
record(await commitAnnotations(session, String(resourceId$1), annotations));
|
|
11633
|
+
}
|
|
11497
11634
|
);
|
|
11498
11635
|
if (signal?.aborted) {
|
|
11499
11636
|
await emitEvent(session, "job:cancel", {
|
|
11500
|
-
...
|
|
11637
|
+
...terminalBase(),
|
|
11501
11638
|
...committed.length > 0 ? { completedUnits: [...committed] } : {}
|
|
11502
11639
|
});
|
|
11503
11640
|
adapter.completeJob();
|
|
11504
11641
|
return;
|
|
11505
11642
|
}
|
|
11506
11643
|
await emitEvent(session, "job:complete", {
|
|
11507
|
-
...
|
|
11644
|
+
...terminalBase(),
|
|
11508
11645
|
result
|
|
11509
11646
|
});
|
|
11510
11647
|
adapter.completeJob();
|
|
11511
11648
|
} else if (jobType === "tag-annotation") {
|
|
11512
|
-
const {
|
|
11649
|
+
const { result } = await processTagJob(
|
|
11513
11650
|
ready.text,
|
|
11514
11651
|
inferenceClient,
|
|
11515
11652
|
asJobParams(job.params),
|
|
11516
11653
|
ready.buildAnnotation,
|
|
11517
|
-
onProgress
|
|
11654
|
+
onProgress,
|
|
11655
|
+
// The durability write, per chunk, awaited; folds into the terminal
|
|
11656
|
+
// durability evidence like every commit.
|
|
11657
|
+
async (annotations) => {
|
|
11658
|
+
record(await commitAnnotations(session, String(resourceId$1), annotations));
|
|
11659
|
+
}
|
|
11518
11660
|
);
|
|
11519
|
-
await commitAnnotations(session, String(resourceId$1), annotations);
|
|
11520
11661
|
await emitEvent(session, "job:complete", {
|
|
11521
|
-
...
|
|
11662
|
+
...terminalBase(),
|
|
11522
11663
|
result
|
|
11523
11664
|
});
|
|
11524
11665
|
adapter.completeJob();
|
|
@@ -11567,7 +11708,7 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal)
|
|
|
11567
11708
|
},
|
|
11568
11709
|
generator
|
|
11569
11710
|
);
|
|
11570
|
-
await commitAnnotations(session, String(resourceId$1), [provenanceRef]);
|
|
11711
|
+
record(await commitAnnotations(session, String(resourceId$1), [provenanceRef]));
|
|
11571
11712
|
}
|
|
11572
11713
|
const citationRefs = [];
|
|
11573
11714
|
if (genResult.format === "application/pdf" && genResult.citations.length > 0) {
|
|
@@ -11621,9 +11762,9 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal)
|
|
|
11621
11762
|
citationRefs.push(citationRef);
|
|
11622
11763
|
}
|
|
11623
11764
|
}
|
|
11624
|
-
await commitAnnotations(session, String(newResourceId), citationRefs);
|
|
11765
|
+
record(await commitAnnotations(session, String(newResourceId), citationRefs));
|
|
11625
11766
|
await emitEvent(session, "job:complete", {
|
|
11626
|
-
...
|
|
11767
|
+
...terminalBase(),
|
|
11627
11768
|
result: { kind: "generation", resourceId: newResourceId, resourceName: genResult.title, truncated: genResult.result.truncated }
|
|
11628
11769
|
});
|
|
11629
11770
|
adapter.completeJob();
|
|
@@ -11684,7 +11825,12 @@ var WORKER_AWAITED_OPERATIONS = [
|
|
|
11684
11825
|
// P6). The worker AWAITS this one — a unit may not advance until its
|
|
11685
11826
|
// annotations are in the event log — so its replies must be in the narrow
|
|
11686
11827
|
// channel set or every commit fails fast with `bus.unsubscribed`.
|
|
11687
|
-
"mark:commit"
|
|
11828
|
+
"mark:commit",
|
|
11829
|
+
// The durability probe for a commit whose acknowledgement never routed
|
|
11830
|
+
// (COMMIT-ACK-FALSE-FAILURE F1). SINGULAR by design: the annotation LIST
|
|
11831
|
+
// channel is the multi-MB fan-out this narrowing exists to keep out, and a
|
|
11832
|
+
// rare error path is no reason to let it back in.
|
|
11833
|
+
"browse:annotation-requested"
|
|
11688
11834
|
];
|
|
11689
11835
|
var WORKER_CHANNELS = replyChannelsFor(WORKER_AWAITED_OPERATIONS);
|
|
11690
11836
|
function parseGatewayUrl(url) {
|
|
@@ -11888,8 +12034,9 @@ for (const jobType of ALL_JOB_TYPES) {
|
|
|
11888
12034
|
group.jobTypes.push(jobType);
|
|
11889
12035
|
}
|
|
11890
12036
|
async function main() {
|
|
11891
|
-
const { initObservabilityNode } = await import('@semiont/observability/node');
|
|
12037
|
+
const { initObservabilityNode, registerSupervisorRestartCount } = await import('@semiont/observability/node');
|
|
11892
12038
|
initObservabilityNode({ serviceName: "semiont-worker" });
|
|
12039
|
+
registerSupervisorRestartCount();
|
|
11893
12040
|
logger.info("Starting agents", {
|
|
11894
12041
|
baseUrl: gatewayBaseUrl,
|
|
11895
12042
|
agents: Array.from(groups.values()).map((g) => ({
|