@semiont/jobs 0.6.7 → 0.6.9

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.
@@ -1,4 +1,4 @@
1
- import { replyChannelsFor, createTomlConfigLoader, didToAgent, baseUrl, STARTUP_FETCH_RETRY, retryWithBackoff, serviceAccountToken, isTransientFetchError, isObject, isString, isNumber, RETRY_RULES, resourceId, getPrimaryMediaType, isGenerationJobParams, capabilitiesOf, assembleAnnotation, findClaimSpan, busRequest, BusRequestError, isArray, textSourceOf, yieldsGeometryOf, decodeRepresentation, GENERATABLE_MEDIA_TYPES, locate, createFragmentSelector, deriveViews, annotationId, estimateTokens, reconcileSelector, getLocaleEnglishName, cutChunk, chunkText } from '@semiont/core';
1
+ import { replyChannelsFor, createTomlConfigLoader, JOB_TYPES, didToAgent, baseUrl, STARTUP_FETCH_RETRY, retryWithBackoff, serviceAccountToken, isTransientFetchError, isObject, isString, isNumber, RETRY_RULES, getPrimaryMediaType, isGenerationJobParams, capabilitiesOf, assembleAnnotation, findClaimSpan, busRequest, BusRequestError, textSourceOf, yieldsGeometryOf, decodeRepresentation, GENERATABLE_MEDIA_TYPES, locate, createFragmentSelector, deriveViews, isResourceId, estimateTokens, reconcileSelector, getLocaleEnglishName, cutChunk, chunkText } 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, answerLimitsRequests, StructuredReadError } from '@semiont/inference';
@@ -3766,9 +3766,9 @@ var require_mapOneOrManyArgs = __commonJS({
3766
3766
  Object.defineProperty(exports, "__esModule", { value: true });
3767
3767
  exports.mapOneOrManyArgs = void 0;
3768
3768
  var map_1 = require_map();
3769
- var isArray2 = Array.isArray;
3769
+ var isArray = Array.isArray;
3770
3770
  function callOrApply(fn, args) {
3771
- return isArray2(args) ? fn.apply(void 0, __spreadArray([], __read(args))) : fn(args);
3771
+ return isArray(args) ? fn.apply(void 0, __spreadArray([], __read(args))) : fn(args);
3772
3772
  }
3773
3773
  function mapOneOrManyArgs(fn) {
3774
3774
  return map_1.map(function(args) {
@@ -3913,14 +3913,14 @@ var require_argsArgArrayOrObject = __commonJS({
3913
3913
  "../../node_modules/rxjs/dist/cjs/internal/util/argsArgArrayOrObject.js"(exports) {
3914
3914
  Object.defineProperty(exports, "__esModule", { value: true });
3915
3915
  exports.argsArgArrayOrObject = void 0;
3916
- var isArray2 = Array.isArray;
3916
+ var isArray = Array.isArray;
3917
3917
  var getPrototypeOf = Object.getPrototypeOf;
3918
3918
  var objectProto = Object.prototype;
3919
3919
  var getKeys = Object.keys;
3920
3920
  function argsArgArrayOrObject(args) {
3921
3921
  if (args.length === 1) {
3922
3922
  var first_1 = args[0];
3923
- if (isArray2(first_1)) {
3923
+ if (isArray(first_1)) {
3924
3924
  return { args: first_1, keys: null };
3925
3925
  }
3926
3926
  if (isPOJO(first_1)) {
@@ -4668,9 +4668,9 @@ var require_argsOrArgArray = __commonJS({
4668
4668
  "../../node_modules/rxjs/dist/cjs/internal/util/argsOrArgArray.js"(exports) {
4669
4669
  Object.defineProperty(exports, "__esModule", { value: true });
4670
4670
  exports.argsOrArgArray = void 0;
4671
- var isArray2 = Array.isArray;
4671
+ var isArray = Array.isArray;
4672
4672
  function argsOrArgArray(args) {
4673
- return args.length === 1 && isArray2(args[0]) ? args[0] : args;
4673
+ return args.length === 1 && isArray(args[0]) ? args[0] : args;
4674
4674
  }
4675
4675
  exports.argsOrArgArray = argsOrArgArray;
4676
4676
  }
@@ -9208,19 +9208,6 @@ var require_cjs = __commonJS({
9208
9208
 
9209
9209
  // src/job-claim-adapter.ts
9210
9210
  var import_rxjs = __toESM(require_cjs());
9211
- function readUnitCursors(raw, completedUnits) {
9212
- if (!isObject(raw)) return {};
9213
- const done = new Set(completedUnits);
9214
- const cursors = {};
9215
- for (const [unit, value] of Object.entries(raw)) {
9216
- if (done.has(unit) || !isObject(value)) continue;
9217
- const { next, size, found, emitted } = value;
9218
- if (!isNumber(next) || !isNumber(size) || next < 0 || size < 1) continue;
9219
- if (!isNumber(found) || !isNumber(emitted) || found < 0 || emitted < 0) continue;
9220
- cursors[unit] = { next, size, found, emitted };
9221
- }
9222
- return cursors;
9223
- }
9224
9211
  function createJobClaimAdapter(options) {
9225
9212
  const { bus, jobTypes } = options;
9226
9213
  const activeJob$ = new import_rxjs.BehaviorSubject(null);
@@ -9239,9 +9226,9 @@ function createJobClaimAdapter(options) {
9239
9226
  let activeSince = null;
9240
9227
  const iso = (t) => t === null ? null : new Date(t).toISOString();
9241
9228
  const claimNext = async () => {
9242
- let record;
9229
+ let claimed;
9243
9230
  try {
9244
- record = await busRequest(bus, "job:claim", { types: jobTypes }, 1e4);
9231
+ claimed = await busRequest(bus, "job:claim", { types: jobTypes }, 1e4);
9245
9232
  } catch (error) {
9246
9233
  if (error instanceof BusRequestError) {
9247
9234
  if (error.code === "bus.none-pending") return { declined: true };
@@ -9249,25 +9236,19 @@ function createJobClaimAdapter(options) {
9249
9236
  }
9250
9237
  return { refused: { code: null, message: error instanceof Error ? error.message : String(error) } };
9251
9238
  }
9252
- if (!isString(record.metadata?.id) || !isString(record.metadata?.type)) {
9253
- return { refused: { code: null, message: "claimed record carries no job id or type" } };
9254
- }
9255
- const completedUnits = isArray(record.metadata?.completedUnits) ? record.metadata.completedUnits.filter(isString) : [];
9256
- const unitCursors = readUnitCursors(record.metadata?.unitCursors, completedUnits);
9257
- const params = record.params ?? {};
9239
+ const { metadata, params } = claimed;
9258
9240
  return {
9259
9241
  job: {
9260
- jobId: record.metadata.id,
9261
- type: record.metadata.type,
9262
- resourceId: isString(params.resourceId) ? params.resourceId : "",
9263
- userId: record.metadata?.userId ?? "",
9242
+ jobId: metadata.id,
9243
+ type: metadata.type,
9244
+ resourceId: params.resourceId,
9264
9245
  params,
9265
- completedUnits,
9266
- unitCursors,
9267
- // Absent or malformed metadata reads as "no budget left" — a worker
9268
- // that cannot see the budget must not claim a retry is coming.
9269
- retryCount: isNumber(record.metadata?.retryCount) ? record.metadata.retryCount : 0,
9270
- maxRetries: isNumber(record.metadata?.maxRetries) ? record.metadata.maxRetries : 0
9246
+ // Both appear on the record once an attempt has checkpointed, and a
9247
+ // finished unit has no cursor: the dispatcher drops it at the merge.
9248
+ completedUnits: metadata.completedUnits ?? [],
9249
+ unitCursors: metadata.unitCursors ?? {},
9250
+ retryCount: metadata.retryCount,
9251
+ maxRetries: metadata.maxRetries
9271
9252
  }
9272
9253
  };
9273
9254
  };
@@ -9386,17 +9367,6 @@ function willRetryAfter(metadata, failureClass) {
9386
9367
  }
9387
9368
 
9388
9369
  // src/types.ts
9389
- var JOB_TYPES = /* @__PURE__ */ new Set([
9390
- "reference-annotation",
9391
- "generation",
9392
- "highlight-annotation",
9393
- "assessment-annotation",
9394
- "comment-annotation",
9395
- "tag-annotation"
9396
- ]);
9397
- function isJobType(value) {
9398
- return JOB_TYPES.has(value);
9399
- }
9400
9370
  function asJobParams(params) {
9401
9371
  if (typeof params.resourceId !== "string") {
9402
9372
  throw new Error("Job params are missing a resourceId");
@@ -10761,8 +10731,6 @@ function compileTypst(source) {
10761
10731
  rmSync(dir, { recursive: true, force: true });
10762
10732
  }
10763
10733
  }
10764
-
10765
- // src/workers/generation/citation-resolver.ts
10766
10734
  var CITATION_TOKEN = /\[\[([^\s[\]/]+)(?:\/([^\s[\]/]+))?\]\]/g;
10767
10735
  function collectContextResourceIds(context) {
10768
10736
  const ids = /* @__PURE__ */ new Set();
@@ -10788,7 +10756,7 @@ function resolveCitationTokens(content, validResourceIds, logger2) {
10788
10756
  const annotationId = match[2];
10789
10757
  clean += content.slice(last, match.index).replace(/[ \t]+$/, "");
10790
10758
  last = match.index + token.length;
10791
- if (!validResourceIds.has(citedResourceId)) {
10759
+ if (!isResourceId(citedResourceId) || !validResourceIds.has(citedResourceId)) {
10792
10760
  logger2.warn("Citation token references an id absent from the provided context \u2014 dropped", {
10793
10761
  resourceId: citedResourceId
10794
10762
  });
@@ -11382,12 +11350,7 @@ async function processGenerationJob(inferenceClient, params, onProgress, logger2
11382
11350
  title,
11383
11351
  format: outputMediaType,
11384
11352
  citations: citations2,
11385
- result: {
11386
- kind: "generation",
11387
- resourceId: "",
11388
- resourceName: title,
11389
- truncated: generated2.truncated
11390
- }
11353
+ truncated: generated2.truncated
11391
11354
  };
11392
11355
  }
11393
11356
  onProgress(5, { code: "generating-resource" });
@@ -11423,12 +11386,7 @@ async function processGenerationJob(inferenceClient, params, onProgress, logger2
11423
11386
  title,
11424
11387
  format: outputMediaType,
11425
11388
  citations,
11426
- result: {
11427
- kind: "generation",
11428
- resourceId: "",
11429
- resourceName: title,
11430
- truncated: generated.truncated
11431
- }
11389
+ truncated: generated.truncated
11432
11390
  };
11433
11391
  }
11434
11392
 
@@ -11512,11 +11470,11 @@ var CommitDurabilityError = class extends Error {
11512
11470
  durability;
11513
11471
  name = "CommitDurabilityError";
11514
11472
  };
11515
- async function probeDurability(session, resourceId$1, annotations) {
11473
+ async function probeDurability(session, resourceId, annotations) {
11516
11474
  const last = annotations[annotations.length - 1];
11517
11475
  if (!last) return "probe-unreachable";
11518
11476
  try {
11519
- await session.client.browse.annotation(resourceId(resourceId$1), annotationId(String(last.id))).fresh();
11477
+ await session.client.browse.annotation(resourceId, last.id).fresh();
11520
11478
  return "probe-confirmed";
11521
11479
  } catch (error) {
11522
11480
  const code = isObject(error) && isString(error.code) ? error.code : void 0;
@@ -11573,32 +11531,30 @@ function startWorkerProcess(config) {
11573
11531
  completedUnitsByJob.delete(job.jobId);
11574
11532
  unitCursorsByJob.delete(job.jobId);
11575
11533
  const failAnnotationId = referenceIdOf(job);
11576
- if (isJobType(job.type)) {
11577
- emitEvent(session, "job:fail", {
11578
- resourceId: job.resourceId,
11579
- jobId: job.jobId,
11580
- jobType: job.type,
11581
- ...failAnnotationId ? { annotationId: failAnnotationId } : {},
11582
- error: message,
11583
- ...completedUnits && completedUnits.length > 0 ? { completedUnits } : {},
11584
- // Where each unfinished unit got to. Absent rather than `{}` when
11585
- // nothing was reached: an empty object would claim units were
11586
- // tracked and none progressed.
11587
- ...unitCursors && Object.keys(unitCursors).length > 0 ? { unitCursors } : {},
11588
- ...failureClass !== void 0 ? { failureClass } : {},
11589
- // What the commit path OBSERVED about durability, when the failure
11590
- // came from a commit at all. Present only on that path: absent means
11591
- // the question never arose, never that durability was ruled out.
11592
- ...error instanceof CommitDurabilityError ? { durability: error.durability } : {},
11593
- // Whether this failure is the END, answered by the same predicate
11594
- // the queue applies at failJob (JOB-RESTART-SAFETY P5). Without
11595
- // it a client cannot tell a recovering run from a dead one: it
11596
- // sees job:fail either way and would end its stream on a job the
11597
- // queue is about to re-run.
11598
- willRetry: willRetryAfter(job, failureClass)
11599
- }).catch(() => {
11600
- });
11601
- }
11534
+ emitEvent(session, "job:fail", {
11535
+ resourceId: job.resourceId,
11536
+ jobId: job.jobId,
11537
+ jobType: job.type,
11538
+ ...failAnnotationId ? { annotationId: failAnnotationId } : {},
11539
+ error: message,
11540
+ ...completedUnits && completedUnits.length > 0 ? { completedUnits } : {},
11541
+ // Where each unfinished unit got to. Absent rather than `{}` when
11542
+ // nothing was reached: an empty object would claim units were
11543
+ // tracked and none progressed.
11544
+ ...unitCursors && Object.keys(unitCursors).length > 0 ? { unitCursors } : {},
11545
+ ...failureClass !== void 0 ? { failureClass } : {},
11546
+ // What the commit path OBSERVED about durability, when the failure
11547
+ // came from a commit at all. Present only on that path: absent means
11548
+ // the question never arose, never that durability was ruled out.
11549
+ ...error instanceof CommitDurabilityError ? { durability: error.durability } : {},
11550
+ // Whether this failure is the END, answered by the same predicate
11551
+ // the queue applies at failJob (JOB-RESTART-SAFETY P5). Without
11552
+ // it a client cannot tell a recovering run from a dead one: it
11553
+ // sees job:fail either way and would end its stream on a job the
11554
+ // queue is about to re-run.
11555
+ willRetry: willRetryAfter(job, failureClass)
11556
+ }).catch(() => {
11557
+ });
11602
11558
  adapter.failJob(job.jobId, message);
11603
11559
  }).finally(() => {
11604
11560
  if (activeCancel?.jobId === job.jobId) activeCancel = null;
@@ -11632,17 +11588,11 @@ async function handleJob(adapter, config, job, completedUnitsByJob = /* @__PURE_
11632
11588
  }
11633
11589
  async function handleJobInner(adapter, config, job, completedUnitsByJob, signal, unitCursorsByJob = /* @__PURE__ */ new Map()) {
11634
11590
  const { session, inferenceClient, generator } = config;
11635
- const { jobId } = job;
11636
- if (!isJobType(job.type)) {
11637
- adapter.failJob(jobId, `Unrecognized job type: ${job.type}`);
11638
- return;
11639
- }
11640
- const jobType = job.type;
11641
- const resourceId$1 = resourceId(job.resourceId);
11591
+ const { jobId, type: jobType, resourceId } = job;
11642
11592
  const annotationId = referenceIdOf(job);
11643
11593
  const attempt = job.retryCount + 1;
11644
11594
  const lifecycleBase = {
11645
- resourceId: resourceId$1,
11595
+ resourceId,
11646
11596
  jobId,
11647
11597
  jobType,
11648
11598
  attempt,
@@ -11661,22 +11611,22 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal,
11661
11611
  }
11662
11612
  let ready = null;
11663
11613
  if (jobType !== "generation") {
11664
- const descriptor = await session.client.browse.resource(resourceId$1).fresh();
11614
+ const descriptor = await session.client.browse.resource(resourceId).fresh();
11665
11615
  const mediaType = getPrimaryMediaType(descriptor);
11666
11616
  const source = await withSpan(
11667
11617
  "detection:prepare",
11668
- () => prepareDetection(mediaType ?? "", config.contentReads, resourceId$1, generator, (rid) => session.client.browse.resourceAnchoredText(rid)),
11669
- { attrs: { "resource.id": resourceId$1, "media.type": mediaType ?? "unknown" } }
11618
+ () => prepareDetection(mediaType ?? "", config.contentReads, resourceId, generator, (rid) => session.client.browse.resourceAnchoredText(rid)),
11619
+ { attrs: { "resource.id": resourceId, "media.type": mediaType ?? "unknown" } }
11670
11620
  );
11671
11621
  if ("declined" in source) {
11672
11622
  if (source.declined === "not-yet") {
11673
- throw new Error(`Anchored text not yet derived for resource ${resourceId$1} \u2014 Smelter has not settled; retrying`);
11623
+ throw new Error(`Anchored text not yet derived for resource ${resourceId} \u2014 Smelter has not settled; retrying`);
11674
11624
  }
11675
11625
  if (source.declined === "no-extractor") {
11676
- throw new DeterministicJobError(`Cannot run ${jobType} on resource ${resourceId$1}: media type '${mediaType ?? "unknown"}' has no extractable text to analyze`);
11626
+ throw new DeterministicJobError(`Cannot run ${jobType} on resource ${resourceId}: media type '${mediaType ?? "unknown"}' has no extractable text to analyze`);
11677
11627
  }
11678
11628
  if (source.declined === "no-map" || source.declined === "unknown") {
11679
- throw new DeterministicJobError(`Cannot run ${jobType} on resource ${resourceId$1}: anchored-text consult returned '${source.declined}'`);
11629
+ throw new DeterministicJobError(`Cannot run ${jobType} on resource ${resourceId}: anchored-text consult returned '${source.declined}'`);
11680
11630
  }
11681
11631
  await emitEvent(session, "job:complete", {
11682
11632
  ...terminalBase(),
@@ -11707,7 +11657,7 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal,
11707
11657
  };
11708
11658
  const unitCursors = /* @__PURE__ */ new Map();
11709
11659
  const commitChunk = async (annotations, checkpoint) => {
11710
- record(await commitAnnotations(session, String(resourceId$1), annotations, jobId));
11660
+ record(await commitAnnotations(session, resourceId, annotations, jobId));
11711
11661
  unitCursors.set(checkpoint.unit, checkpoint.cursor);
11712
11662
  unitCursorsByJob.set(job.jobId, Object.fromEntries(unitCursors));
11713
11663
  await emitEvent(session, "job:checkpoint", {
@@ -11868,7 +11818,7 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal,
11868
11818
  file: Buffer.from(genResult.content),
11869
11819
  format: genResult.format,
11870
11820
  storageUri,
11871
- sourceResourceId: resourceId$1,
11821
+ sourceResourceId: resourceId,
11872
11822
  ...genReferenceId ? { sourceAnnotationId: genReferenceId } : {},
11873
11823
  ...genParams.prompt ? { generationPrompt: genParams.prompt } : {},
11874
11824
  ...genParams.language ? { language: genParams.language } : {},
@@ -11882,12 +11832,12 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal,
11882
11832
  const { annotation: provenanceRef } = assembleAnnotation(
11883
11833
  {
11884
11834
  motivation: "linking",
11885
- target: { source: String(resourceId$1) },
11886
- body: { type: "SpecificResource", source: String(newResourceId), purpose: "linking" }
11835
+ target: { source: resourceId },
11836
+ body: { type: "SpecificResource", source: newResourceId, purpose: "linking" }
11887
11837
  },
11888
11838
  generator
11889
11839
  );
11890
- record(await commitAnnotations(session, String(resourceId$1), [provenanceRef], jobId));
11840
+ record(await commitAnnotations(session, resourceId, [provenanceRef], jobId));
11891
11841
  }
11892
11842
  const citationRefs = [];
11893
11843
  if (genResult.format === "application/pdf" && genResult.citations.length > 0) {
@@ -11912,7 +11862,7 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal,
11912
11862
  }
11913
11863
  const citationRef = buildPdfAnnotation(
11914
11864
  layer,
11915
- resourceId(String(newResourceId)),
11865
+ newResourceId,
11916
11866
  generator,
11917
11867
  "linking",
11918
11868
  { exact: layer.text.slice(span.start, span.end), start: span.start, end: span.end },
@@ -11927,7 +11877,7 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal,
11927
11877
  {
11928
11878
  motivation: "linking",
11929
11879
  target: {
11930
- source: String(newResourceId),
11880
+ source: newResourceId,
11931
11881
  selector: [
11932
11882
  { type: "TextPositionSelector", start: citation.start, end: citation.end },
11933
11883
  { type: "TextQuoteSelector", exact: citation.exact }
@@ -11940,10 +11890,10 @@ async function handleJobInner(adapter, config, job, completedUnitsByJob, signal,
11940
11890
  citationRefs.push(citationRef);
11941
11891
  }
11942
11892
  }
11943
- record(await commitAnnotations(session, String(newResourceId), citationRefs, jobId));
11893
+ record(await commitAnnotations(session, newResourceId, citationRefs, jobId));
11944
11894
  await emitEvent(session, "job:complete", {
11945
11895
  ...terminalBase(),
11946
- result: { kind: "generation", resourceId: newResourceId, resourceName: genResult.title, truncated: genResult.result.truncated }
11896
+ result: { kind: "generation", resourceId: newResourceId, resourceName: genResult.title, truncated: genResult.truncated }
11947
11897
  });
11948
11898
  adapter.completeJob();
11949
11899
  } else {
@@ -12163,14 +12113,6 @@ async function startAgentWorker(opts) {
12163
12113
  }
12164
12114
  };
12165
12115
  }
12166
- var ALL_JOB_TYPES = [
12167
- "reference-annotation",
12168
- "generation",
12169
- "highlight-annotation",
12170
- "assessment-annotation",
12171
- "comment-annotation",
12172
- "tag-annotation"
12173
- ];
12174
12116
  var configPath = join(homedir(), ".semiontconfig");
12175
12117
  var tomlReader = {
12176
12118
  readIfExists: (p) => existsSync(p) ? readFileSync(p, "utf-8") : null
@@ -12227,7 +12169,7 @@ function toClientConfig(w) {
12227
12169
  };
12228
12170
  }
12229
12171
  var groups = /* @__PURE__ */ new Map();
12230
- for (const jobType of ALL_JOB_TYPES) {
12172
+ for (const jobType of JOB_TYPES) {
12231
12173
  const inference = resolveWorker(jobType);
12232
12174
  const key = clientKey(inference);
12233
12175
  let group = groups.get(key);