@semiont/jobs 0.5.27 → 0.5.29
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 +4 -2
- package/dist/index.d.ts +15 -3
- package/dist/index.js +39 -24
- package/dist/index.js.map +1 -1
- package/dist/worker-main.js +101 -50
- package/dist/worker-main.js.map +1 -1
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Job queue, worker infrastructure, and annotation workers for [Semiont](https://g
|
|
|
10
10
|
|
|
11
11
|
## Architecture Context
|
|
12
12
|
|
|
13
|
-
Workers run in a separate process and connect to the Knowledge System (KS) over HTTP/SSE using a `SemiontSession` (from `@semiont/sdk`) driven by a `JobClaimAdapter`. Workers receive job assignments via an SSE `job:queued` subscription, claim jobs atomically, and emit domain events back to the KS via `session.client.transport.emit(...)`. The KS ingests these events onto its EventBus for SSE delivery to the
|
|
13
|
+
Workers run in a separate process and connect to the Knowledge System (KS) over HTTP/SSE using a `SemiontSession` (from `@semiont/sdk`) driven by a `JobClaimAdapter`. Workers receive job assignments via an SSE `job:queued` subscription, claim jobs atomically, and emit domain events back to the KS via `session.client.transport.emit(...)`. The KS ingests these events onto its EventBus for SSE delivery to the Browser.
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
@@ -36,7 +36,9 @@ import { SemiontProject } from '@semiont/core/node';
|
|
|
36
36
|
|
|
37
37
|
// Initialize — jobs are stored under project.jobsDir
|
|
38
38
|
const eventBus = new EventBus();
|
|
39
|
-
const project = new SemiontProject('/path/to/project'
|
|
39
|
+
const project = new SemiontProject('/path/to/project', {
|
|
40
|
+
anchoredTextDir: process.env.SEMIONT_ANCHORED_TEXT_DIR!,
|
|
41
|
+
});
|
|
40
42
|
const jobQueue = new FsJobQueue(project, logger, eventBus);
|
|
41
43
|
await jobQueue.initialize();
|
|
42
44
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JobId, UserId, ResourceId, EntityType, GenerationJobParams, TagSchema, Logger, EventBus, components, Annotation, SupportedMediaType, GatheredContext } from '@semiont/core';
|
|
2
|
-
import {
|
|
2
|
+
import { SemiontState } from '@semiont/core/node';
|
|
3
3
|
import { InferenceClient } from '@semiont/inference';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -136,6 +136,7 @@ interface DetectionProgress {
|
|
|
136
136
|
* Detection job result
|
|
137
137
|
*/
|
|
138
138
|
interface DetectionResult {
|
|
139
|
+
kind: 'reference-annotation';
|
|
139
140
|
totalFound: number;
|
|
140
141
|
totalEmitted: number;
|
|
141
142
|
errors: number;
|
|
@@ -153,8 +154,11 @@ interface YieldProgress {
|
|
|
153
154
|
* Generation job result
|
|
154
155
|
*/
|
|
155
156
|
interface GenerationResult {
|
|
157
|
+
kind: 'generation';
|
|
156
158
|
resourceId: ResourceId;
|
|
157
159
|
resourceName: string;
|
|
160
|
+
/** True when the model stopped at the maxTokens ceiling — the artifact is cut off, not complete (GENERATE-FROM-RESOURCE D6). */
|
|
161
|
+
truncated: boolean;
|
|
158
162
|
}
|
|
159
163
|
/**
|
|
160
164
|
* Highlight detection job progress
|
|
@@ -168,6 +172,7 @@ interface HighlightDetectionProgress {
|
|
|
168
172
|
* Highlight detection job result
|
|
169
173
|
*/
|
|
170
174
|
interface HighlightDetectionResult {
|
|
175
|
+
kind: 'highlight-annotation';
|
|
171
176
|
highlightsFound: number;
|
|
172
177
|
highlightsCreated: number;
|
|
173
178
|
}
|
|
@@ -183,6 +188,7 @@ interface AssessmentDetectionProgress {
|
|
|
183
188
|
* Assessment detection job result
|
|
184
189
|
*/
|
|
185
190
|
interface AssessmentDetectionResult {
|
|
191
|
+
kind: 'assessment-annotation';
|
|
186
192
|
assessmentsFound: number;
|
|
187
193
|
assessmentsCreated: number;
|
|
188
194
|
}
|
|
@@ -198,6 +204,7 @@ interface CommentDetectionProgress {
|
|
|
198
204
|
* Comment detection job result
|
|
199
205
|
*/
|
|
200
206
|
interface CommentDetectionResult {
|
|
207
|
+
kind: 'comment-annotation';
|
|
201
208
|
commentsFound: number;
|
|
202
209
|
commentsCreated: number;
|
|
203
210
|
}
|
|
@@ -216,6 +223,7 @@ interface TagDetectionProgress {
|
|
|
216
223
|
* Tag detection job result
|
|
217
224
|
*/
|
|
218
225
|
interface TagDetectionResult {
|
|
226
|
+
kind: 'tag-annotation';
|
|
219
227
|
tagsFound: number;
|
|
220
228
|
tagsCreated: number;
|
|
221
229
|
byCategory: Record<string, number>;
|
|
@@ -345,7 +353,10 @@ declare class FsJobQueue implements JobQueue {
|
|
|
345
353
|
private cleanupTimer;
|
|
346
354
|
/** Per-job timestamp of the last progress write, for throttling. */
|
|
347
355
|
private lastProgressWrite;
|
|
348
|
-
constructor(
|
|
356
|
+
constructor(
|
|
357
|
+
/** `SemiontState` and not `SemiontProject`: the queue reads ONE path,
|
|
358
|
+
* and the gateway that owns it mounts no KB tree (SINGLE-KB-MOUNT P5). */
|
|
359
|
+
state: SemiontState, logger: Logger, eventBus?: EventBus | undefined);
|
|
349
360
|
/**
|
|
350
361
|
* Initialize job queue directories, announce any pending backlog,
|
|
351
362
|
* and start the re-announce interval. Idempotent.
|
|
@@ -654,6 +665,7 @@ declare function generateResourceFromTopic(topic: string, entityTypes: string[],
|
|
|
654
665
|
}): Promise<{
|
|
655
666
|
title: string;
|
|
656
667
|
content: string;
|
|
668
|
+
truncated: boolean;
|
|
657
669
|
}>;
|
|
658
670
|
|
|
659
671
|
/**
|
|
@@ -683,7 +695,7 @@ declare function generateResourceFromTopic(topic: string, entityTypes: string[],
|
|
|
683
695
|
*
|
|
684
696
|
* Thresholds are fixed by design (no env knobs) and deliberately
|
|
685
697
|
* layered: inference timeout (10 min, P2) fires first; this watchdog
|
|
686
|
-
* (15 min) catches the failure modes nobody predicted; the
|
|
698
|
+
* (15 min) catches the failure modes nobody predicted; the gateway's
|
|
687
699
|
* dead-worker janitor (30 min) re-queues the job regardless.
|
|
688
700
|
*/
|
|
689
701
|
declare const STALL_THRESHOLD_MS: number;
|
package/dist/index.js
CHANGED
|
@@ -17,9 +17,9 @@ var PROGRESS_WRITE_MIN_INTERVAL_MS = 5e3;
|
|
|
17
17
|
var RETENTION_HOURS = 24;
|
|
18
18
|
var CLEANUP_INTERVAL_MS = 36e5;
|
|
19
19
|
var FsJobQueue = class {
|
|
20
|
-
constructor(
|
|
20
|
+
constructor(state, logger, eventBus) {
|
|
21
21
|
this.eventBus = eventBus;
|
|
22
|
-
this.jobsDir =
|
|
22
|
+
this.jobsDir = state.jobsDir;
|
|
23
23
|
this.logger = logger;
|
|
24
24
|
}
|
|
25
25
|
eventBus;
|
|
@@ -468,9 +468,9 @@ async function withTimeout(work, label, onHeartbeat) {
|
|
|
468
468
|
if (heartbeat) clearInterval(heartbeat);
|
|
469
469
|
}
|
|
470
470
|
}
|
|
471
|
-
function
|
|
471
|
+
function boundedGenerateWithMetadata(client, prompt, maxTokens, temperature, onHeartbeat) {
|
|
472
472
|
return spanned(client, "text", maxTokens, () => withTimeout(
|
|
473
|
-
client.
|
|
473
|
+
client.generateTextWithMetadata(prompt, maxTokens, temperature),
|
|
474
474
|
`${client.type}:${client.modelId}`,
|
|
475
475
|
onHeartbeat
|
|
476
476
|
));
|
|
@@ -1253,6 +1253,7 @@ var SEMANTIC_MATCH_CHARS = 240;
|
|
|
1253
1253
|
function idLabel(resourceId, annotationId) {
|
|
1254
1254
|
return `[${resourceId}${annotationId ? `/${annotationId}` : ""}]`;
|
|
1255
1255
|
}
|
|
1256
|
+
var DEFAULT_MAX_TOKENS = 500;
|
|
1256
1257
|
async function generateResourceFromTopic(topic, entityTypes, client, logger, userPrompt, locale, context, temperature, maxTokens, sourceLanguage, outputMediaType = "text/markdown", task = "resource", structure, cite = false, repair) {
|
|
1257
1258
|
logger.debug("Generating resource from topic", {
|
|
1258
1259
|
topicPreview: topic.substring(0, 100),
|
|
@@ -1268,7 +1269,7 @@ async function generateResourceFromTopic(topic, entityTypes, client, logger, use
|
|
|
1268
1269
|
structure
|
|
1269
1270
|
});
|
|
1270
1271
|
const finalTemperature = temperature ?? 0.7;
|
|
1271
|
-
const finalMaxTokens = maxTokens ??
|
|
1272
|
+
const finalMaxTokens = maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
1272
1273
|
const languageInstruction = locale && locale !== "en" ? `
|
|
1273
1274
|
|
|
1274
1275
|
IMPORTANT: Write the entire resource in ${getLanguageName(locale)}.` : "";
|
|
@@ -1295,6 +1296,9 @@ The source resource and embedded context are in ${getLanguageName(sourceLanguage
|
|
|
1295
1296
|
parts.push(`- ${label}: ${bodyItem.value}`);
|
|
1296
1297
|
}
|
|
1297
1298
|
}
|
|
1299
|
+
if (focus.userHint) {
|
|
1300
|
+
parts.push(`- User hint (steers what to generate): ${focus.userHint}`);
|
|
1301
|
+
}
|
|
1298
1302
|
annotationSection = `
|
|
1299
1303
|
|
|
1300
1304
|
Annotation context:
|
|
@@ -1453,16 +1457,16 @@ ${formatRequirements}`;
|
|
|
1453
1457
|
temperature: finalTemperature,
|
|
1454
1458
|
maxTokens: finalMaxTokens
|
|
1455
1459
|
});
|
|
1456
|
-
const response = await
|
|
1457
|
-
logger.debug("Got response from inference", { responseLength: response.length });
|
|
1458
|
-
const result = parseResponse(response);
|
|
1460
|
+
const response = await boundedGenerateWithMetadata(client, prompt, finalMaxTokens, finalTemperature);
|
|
1461
|
+
logger.debug("Got response from inference", { responseLength: response.text.length, stopReason: response.stopReason });
|
|
1462
|
+
const result = parseResponse(response.text);
|
|
1459
1463
|
logger.debug("Parsed response", {
|
|
1460
1464
|
hasTitle: !!result.title,
|
|
1461
1465
|
titleLength: result.title?.length,
|
|
1462
1466
|
hasContent: !!result.content,
|
|
1463
1467
|
contentLength: result.content?.length
|
|
1464
1468
|
});
|
|
1465
|
-
return result;
|
|
1469
|
+
return { ...result, truncated: response.stopReason === "max_tokens" };
|
|
1466
1470
|
}
|
|
1467
1471
|
var PINNED_CREATION_TIMESTAMP = 17e8;
|
|
1468
1472
|
var MAX_COMPILE_REPAIRS = 2;
|
|
@@ -1602,7 +1606,7 @@ async function processHighlightJob(content, inferenceClient, params, buildAnnota
|
|
|
1602
1606
|
onProgress(100, { code: "complete-created", count: annotations.length, kind: "highlight" }, echo);
|
|
1603
1607
|
return {
|
|
1604
1608
|
annotations,
|
|
1605
|
-
result: { highlightsFound: highlights.length, highlightsCreated: annotations.length }
|
|
1609
|
+
result: { kind: "highlight-annotation", highlightsFound: highlights.length, highlightsCreated: annotations.length }
|
|
1606
1610
|
};
|
|
1607
1611
|
}
|
|
1608
1612
|
function detectionEcho(p) {
|
|
@@ -1642,7 +1646,7 @@ async function processCommentJob(content, inferenceClient, params, buildAnnotati
|
|
|
1642
1646
|
onProgress(100, { code: "complete-created", count: annotations.length, kind: "comment" }, echo);
|
|
1643
1647
|
return {
|
|
1644
1648
|
annotations,
|
|
1645
|
-
result: { commentsFound: comments.length, commentsCreated: annotations.length }
|
|
1649
|
+
result: { kind: "comment-annotation", commentsFound: comments.length, commentsCreated: annotations.length }
|
|
1646
1650
|
};
|
|
1647
1651
|
}
|
|
1648
1652
|
async function processAssessmentJob(content, inferenceClient, params, buildAnnotation, onProgress) {
|
|
@@ -1682,7 +1686,7 @@ async function processAssessmentJob(content, inferenceClient, params, buildAnnot
|
|
|
1682
1686
|
onProgress(100, { code: "complete-created", count: annotations.length, kind: "assessment" }, echo);
|
|
1683
1687
|
return {
|
|
1684
1688
|
annotations,
|
|
1685
|
-
result: { assessmentsFound: assessments.length, assessmentsCreated: annotations.length }
|
|
1689
|
+
result: { kind: "assessment-annotation", assessmentsFound: assessments.length, assessmentsCreated: annotations.length }
|
|
1686
1690
|
};
|
|
1687
1691
|
}
|
|
1688
1692
|
async function processReferenceJob(content, inferenceClient, params, buildAnnotation, onProgress, logger) {
|
|
@@ -1772,7 +1776,7 @@ async function processReferenceJob(content, inferenceClient, params, buildAnnota
|
|
|
1772
1776
|
onProgress(100, { code: "complete-created", count: annotations.length, kind: "reference" }, { requestParams });
|
|
1773
1777
|
return {
|
|
1774
1778
|
annotations,
|
|
1775
|
-
result: { totalFound, totalEmitted: annotations.length, errors }
|
|
1779
|
+
result: { kind: "reference-annotation", totalFound, totalEmitted: annotations.length, errors }
|
|
1776
1780
|
};
|
|
1777
1781
|
}
|
|
1778
1782
|
async function processTagJob(content, inferenceClient, params, buildAnnotation, onProgress) {
|
|
@@ -1829,7 +1833,7 @@ async function processTagJob(content, inferenceClient, params, buildAnnotation,
|
|
|
1829
1833
|
onProgress(100, { code: "complete-created", count: annotations.length, kind: "tag" });
|
|
1830
1834
|
return {
|
|
1831
1835
|
annotations,
|
|
1832
|
-
result: { tagsFound: tags.length, tagsCreated: annotations.length, byCategory }
|
|
1836
|
+
result: { kind: "tag-annotation", tagsFound: tags.length, tagsCreated: annotations.length, byCategory }
|
|
1833
1837
|
};
|
|
1834
1838
|
}
|
|
1835
1839
|
function assertWithinOutputBudget(byteLength) {
|
|
@@ -1876,7 +1880,17 @@ async function processGenerationJob(inferenceClient, params, onProgress, logger)
|
|
|
1876
1880
|
}
|
|
1877
1881
|
let compiled = compileTypst(source);
|
|
1878
1882
|
let repairs = 0;
|
|
1879
|
-
while ("error" in compiled
|
|
1883
|
+
while ("error" in compiled) {
|
|
1884
|
+
if (generated2.truncated) {
|
|
1885
|
+
throw new Error(
|
|
1886
|
+
`Generation stopped at the maxTokens ceiling (${params.maxTokens ?? DEFAULT_MAX_TOKENS} tokens) and the cut-off Typst source does not compile \u2014 repair cannot help; raise maxTokens. Compile error: ${compiled.error}`
|
|
1887
|
+
);
|
|
1888
|
+
}
|
|
1889
|
+
if (repairs >= MAX_COMPILE_REPAIRS) {
|
|
1890
|
+
throw new Error(
|
|
1891
|
+
`Typst compilation failed after ${MAX_COMPILE_REPAIRS} repair attempts: ${compiled.error}`
|
|
1892
|
+
);
|
|
1893
|
+
}
|
|
1880
1894
|
repairs++;
|
|
1881
1895
|
logger.warn("Typst compile failed \u2014 feeding the error back for repair", {
|
|
1882
1896
|
attempt: repairs,
|
|
@@ -1908,21 +1922,19 @@ async function processGenerationJob(inferenceClient, params, onProgress, logger)
|
|
|
1908
1922
|
}
|
|
1909
1923
|
compiled = compileTypst(source);
|
|
1910
1924
|
}
|
|
1911
|
-
if ("error" in compiled) {
|
|
1912
|
-
throw new Error(
|
|
1913
|
-
`Typst compilation failed after ${MAX_COMPILE_REPAIRS} repair attempts: ${compiled.error}`
|
|
1914
|
-
);
|
|
1915
|
-
}
|
|
1916
1925
|
assertWithinOutputBudget(compiled.pdf.byteLength);
|
|
1917
1926
|
onProgress(95, { code: "creating-resource" });
|
|
1927
|
+
onProgress(100, { code: "complete-generated", truncated: generated2.truncated });
|
|
1918
1928
|
return {
|
|
1919
1929
|
content: compiled.pdf,
|
|
1920
|
-
title
|
|
1930
|
+
title,
|
|
1921
1931
|
format: outputMediaType,
|
|
1922
1932
|
citations: citations2,
|
|
1923
1933
|
result: {
|
|
1934
|
+
kind: "generation",
|
|
1924
1935
|
resourceId: "",
|
|
1925
|
-
resourceName:
|
|
1936
|
+
resourceName: title,
|
|
1937
|
+
truncated: generated2.truncated
|
|
1926
1938
|
}
|
|
1927
1939
|
};
|
|
1928
1940
|
}
|
|
@@ -1953,14 +1965,17 @@ async function processGenerationJob(inferenceClient, params, onProgress, logger)
|
|
|
1953
1965
|
onProgress(95, { code: "creating-resource" });
|
|
1954
1966
|
const artifact = new TextEncoder().encode(content);
|
|
1955
1967
|
assertWithinOutputBudget(artifact.byteLength);
|
|
1968
|
+
onProgress(100, { code: "complete-generated", truncated: generated.truncated });
|
|
1956
1969
|
return {
|
|
1957
1970
|
content: artifact,
|
|
1958
|
-
title
|
|
1971
|
+
title,
|
|
1959
1972
|
format: outputMediaType,
|
|
1960
1973
|
citations,
|
|
1961
1974
|
result: {
|
|
1975
|
+
kind: "generation",
|
|
1962
1976
|
resourceId: "",
|
|
1963
|
-
resourceName:
|
|
1977
|
+
resourceName: title,
|
|
1978
|
+
truncated: generated.truncated
|
|
1964
1979
|
}
|
|
1965
1980
|
};
|
|
1966
1981
|
}
|