@semiont/jobs 0.6.9 → 0.6.11
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 +34 -122
- package/dist/index.d.ts +117 -130
- package/dist/index.js +23 -25
- package/dist/index.js.map +1 -1
- package/dist/worker-main.js +43 -45
- package/dist/worker-main.js.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -6,148 +6,60 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/@semiont/jobs)
|
|
7
7
|
[](https://github.com/The-AI-Alliance/semiont/blob/main/LICENSE)
|
|
8
8
|
|
|
9
|
-
The
|
|
9
|
+
The worker: the process that claims jobs and does them. A job is an annotation pass over a resource (references, highlights, comments, assessments, tags) or the generation of a new resource, done with a model through [`@semiont/inference`](../inference/README.md).
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The queue is not here. It is the [dispatcher](../../apps/dispatcher/README.md)'s, and the protocol between the two is [Jobs](../../docs/protocol/JOBS.md).
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Who uses it
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
- **The Worker service** is this package's `worker-main`, run as the `semiont-worker` image ([apps/worker](../../apps/worker/README.md)).
|
|
16
|
+
- **[`@semiont/make-meaning`](../make-meaning/README.md)** takes one constant from it: the stall threshold that its own gather deadlines are checked against.
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
npm install @semiont/jobs
|
|
19
|
-
```
|
|
18
|
+
**Building an application?** You do not need this package. An application starts a job through [`@semiont/sdk`](../sdk/README.md) (`mark.assist` for an annotation pass, `yield.fromContext` for a generation) and follows it with the `job` namespace.
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
- `@semiont/core` — Core types, `SemiontProject`, EventBus, `BusRequestPrimitive`
|
|
23
|
-
- `@semiont/sdk` — `SemiontSession`, `SemiontClient` (worker process)
|
|
24
|
-
- `@semiont/http-transport` — HTTP transport, OpenAPI types
|
|
25
|
-
- `@semiont/inference` — InferenceClient for AI operations
|
|
26
|
-
- `@semiont/content` — Content storage URI derivation
|
|
27
|
-
- `@semiont/event-sourcing` — Annotation id generation
|
|
28
|
-
- `@semiont/observability` — Spans and job-outcome metrics
|
|
20
|
+
## What is in it
|
|
29
21
|
|
|
30
|
-
|
|
22
|
+
| | |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `@semiont/jobs/worker-main` | The Worker's entry point. It signs in as an agent, opens a client for each model it is configured with, and claims jobs until it is stopped |
|
|
25
|
+
| `createJobClaimAdapter` | Claiming over the bus: `job:claim` whenever the worker is idle, woken by `job:queued` |
|
|
26
|
+
| `processReferenceJob`, `processHighlightJob`, `processCommentJob`, `processAssessmentJob`, `processTagJob`, `processGenerationJob` | One function per job type. There are no worker classes |
|
|
27
|
+
| `AnnotationDetection`, `generateResourceFromTopic` | What the processors call: the detection passes, and generation |
|
|
28
|
+
| `JobType`, `AnyJob`, and each job's params | The job types. `JobType` and `JobStatus` are generated from the spec |
|
|
29
|
+
| `isPendingJob`, `isRunningJob`, `isCompleteJob`, `isFailedJob`, `isCancelledJob` | Guards that narrow a job to its state |
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
client starts one through the SDK — `semiont.mark.assist(...)` for an annotation pass,
|
|
34
|
-
`semiont.yield.fromContext(...)` for generation — and follows it with `semiont.job`. A worker is this
|
|
35
|
-
package's `worker-main`, run as the `semiont-worker` image.
|
|
31
|
+
## Example
|
|
36
32
|
|
|
37
|
-
|
|
33
|
+
A job is a union discriminated by its `status`, so what a job carries follows from the state it is in.
|
|
38
34
|
|
|
39
35
|
```typescript
|
|
40
|
-
type
|
|
41
|
-
| 'reference-annotation' // Entity reference detection
|
|
42
|
-
| 'generation' // AI content generation
|
|
43
|
-
| 'highlight-annotation' // Key passage highlighting
|
|
44
|
-
| 'assessment-annotation' // Evaluative assessments
|
|
45
|
-
| 'comment-annotation' // Explanatory comments
|
|
46
|
-
| 'tag-annotation' // Structural role tagging
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## Job Metadata
|
|
50
|
-
|
|
51
|
-
All jobs share common metadata:
|
|
36
|
+
import { isCompleteJob, isRunningJob, type AnyJob } from '@semiont/jobs';
|
|
52
37
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
type:
|
|
57
|
-
userId: UserId; // Who requested it — the verified DID, the job's only identity
|
|
58
|
-
created: string;
|
|
59
|
-
retryCount: number;
|
|
60
|
-
maxRetries: number;
|
|
61
|
-
completedUnits?: string[]; // Checkpoint: work units already persisted —
|
|
62
|
-
// the retry skips them
|
|
63
|
-
unitCursors?: Record<string, UnitCursor>; // How far each unfinished unit got
|
|
38
|
+
function describe(job: AnyJob): string {
|
|
39
|
+
if (isRunningJob(job)) return `${job.metadata.type}: running`; // job.progress is here, job.result is not
|
|
40
|
+
if (isCompleteJob(job)) return `${job.metadata.type}: done`; // job.result is here
|
|
41
|
+
return `${job.metadata.type}: ${job.status}`;
|
|
64
42
|
}
|
|
65
43
|
```
|
|
66
44
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
## Progress
|
|
70
|
-
|
|
71
|
-
A running job's `progress` is `StoredProgress`: the last `JobProgress` its worker reported with `job:report-progress`, or `{}` before the first report. It is one shape for every job type — the spec's `JobRunning.progress` — so there are no per-type progress types. See [Job Types Guide](./docs/JobTypes.md#progress).
|
|
72
|
-
|
|
73
|
-
## Annotation Workers
|
|
74
|
-
|
|
75
|
-
The worker process (`worker-main.ts` → `startWorkerProcess` in `worker-process.ts`) claims jobs over the bus via a `JobClaimAdapter` and dispatches by `jobType` to a processor function. There are no per-type worker classes; each job type maps to one `process*Job` function:
|
|
76
|
-
|
|
77
|
-
| Job Type | Processor |
|
|
78
|
-
|----------|-----------|
|
|
79
|
-
| `reference-annotation` | `processReferenceJob` |
|
|
80
|
-
| `generation` | `processGenerationJob` |
|
|
81
|
-
| `highlight-annotation` | `processHighlightJob` |
|
|
82
|
-
| `assessment-annotation` | `processAssessmentJob` |
|
|
83
|
-
| `comment-annotation` | `processCommentJob` |
|
|
84
|
-
| `tag-annotation` | `processTagJob` |
|
|
85
|
-
|
|
86
|
-
Detection logic lives in the `AnnotationDetection` class (`src/workers/annotation-detection.ts`); generation synthesis in `generateResourceFromTopic()` (`src/workers/generation/resource-generation.ts`). Processors never fetch content themselves — the worker process prepares it with `prepareDetection` (bytes through `contentReads` for text media, the Smelter's anchored text for geometry-bearing media such as PDF) and passes it in.
|
|
87
|
-
|
|
88
|
-
Workers emit lifecycle events via `session.client.transport.emit('job:start' | 'job:report-progress' | 'job:checkpoint' | 'job:complete' | 'job:fail', payload)` and persist annotations through the **awaited `mark:commit` operation** — a batch per unit of work that resolves only once the Stower actor in @semiont/make-meaning has appended every annotation to the event log. Unit completion and `job:complete` gate on that acknowledgement, never on emission, so a down persistence sink is a retryable failure instead of silent loss. The dispatcher's job command handlers mirror the lifecycle events into the queue (completion, checkpoints, retry-on-failure with `maxRetries`, progress-as-heartbeat). `job:fail` carries the fields the worker computes: `completedUnits` and `unitCursors` (the checkpoint), `failureClass`, and `willRetry` — see Failure discipline.
|
|
89
|
-
|
|
90
|
-
## Failure discipline
|
|
91
|
-
|
|
92
|
-
Long inference work fails in bounded, classified, resumable ways — every piece below was built against a measured production failure, not a hypothetical:
|
|
45
|
+
## What a change must keep
|
|
93
46
|
|
|
94
|
-
- **
|
|
95
|
-
- **
|
|
96
|
-
- **
|
|
97
|
-
- **
|
|
98
|
-
- **
|
|
99
|
-
- **
|
|
100
|
-
- **Retries resume from the checkpoint.** Reference-annotation persists each entity type's annotations as that unit completes; completed units ride `job:checkpoint` and `job:fail` into `metadata.completedUnits`, and the retry processes only what's left.
|
|
47
|
+
- **A worker holds nothing of the knowledge base.** It has no mount and no broker credential. It reaches the gateway with a URL and a token, reads a resource's bytes from the Archivist, and writes annotations by the awaited `mark:commit`, a batch per unit of work.
|
|
48
|
+
- **A worker sees a job only as it is handed over.** It never reads the queue's storage. What it knows of a job is what `job:claimed` carried.
|
|
49
|
+
- **A worker never says who asked.** A job's requester is the identity the gateway verified on `job:create`, recorded by the dispatcher. What the worker contributes is itself, as the software agent that generated the result.
|
|
50
|
+
- **A processor is plain work.** It is given content, an inference client, the job's params and callbacks for progress and for committing a chunk. It fetches nothing and knows no transport.
|
|
51
|
+
- **One progress shape.** Every job type reports the spec's `JobProgress`. There are no per-type progress types.
|
|
52
|
+
- **Failure is bounded, classified and resumable.** Every inference call has a deadline and is truly cancelled. A failure caused by size subdivides the work in place. A retry resumes from the last checkpoint and skips what was committed. [Failure discipline](docs/FailureDiscipline.md) has the rules.
|
|
101
53
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Workers are not subclassed. To add a job type:
|
|
105
|
-
|
|
106
|
-
1. Add the new `JobType` and its params type in `src/types.ts`, and in the spec: the type in `JobType.json`, its category in `specs/src/jobs/storage.json`, and its result schema in the `JobResult` union. There is no progress type — every job reports `JobProgress`.
|
|
107
|
-
2. Add a `process*Job` function in `src/processors.ts` that runs the inference and returns the annotations/result.
|
|
108
|
-
3. Dispatch the new `jobType` to that processor in `handleJobInner()` in `src/worker-process.ts`.
|
|
109
|
-
|
|
110
|
-
Processors are transport-agnostic: they take content, an `InferenceClient`, the job params, a `buildAnnotation` closure (which carries the `generator` — the worker's own `Software` agent), an `onProgress` callback and a per-chunk commit callback, and return a result. No user identity reaches a processor: an annotation states what produced it, and who requested it is derived by the knowledge base from the job the commit cites. The worker process handles claiming, content fetching, committing, and lifecycle event emission.
|
|
111
|
-
|
|
112
|
-
## Discriminated Unions
|
|
113
|
-
|
|
114
|
-
Jobs use TypeScript discriminated unions for type safety:
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
function handleJob(job: AnyJob) {
|
|
118
|
-
if (job.status === 'running') {
|
|
119
|
-
console.log(job.progress); // Available — StoredProgress, `{}` until the first report
|
|
120
|
-
// console.log(job.result); // Compile error
|
|
121
|
-
}
|
|
122
|
-
if (job.status === 'complete') {
|
|
123
|
-
console.log(job.result); // Available
|
|
124
|
-
// console.log(job.progress); // Compile error
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
## Storage Format
|
|
130
|
-
|
|
131
|
-
The dispatcher keeps one `JobRecord` per job in a JetStream key-value bucket, in the layout
|
|
132
|
-
`specs/src/jobs/storage.json` states. A worker never reads it: it sees a job only as the dispatcher
|
|
133
|
-
hands it over on `job:claimed`.
|
|
54
|
+
Adding a job type starts in the spec, not here: [Workers](docs/Workers.md#adding-a-custom-job-type) lists the steps.
|
|
134
55
|
|
|
135
56
|
## Documentation
|
|
136
57
|
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
140
|
-
-
|
|
141
|
-
- **[API Reference](./docs/API.md)** — Complete API reference
|
|
58
|
+
- [Workers](docs/Workers.md): how a worker runs, where content comes from, how it emits, a worker written outside this package, adding a job type.
|
|
59
|
+
- [Job types](docs/JobTypes.md): a job's states, the six types, and what each one's params, result and progress mean.
|
|
60
|
+
- [Failure discipline](docs/FailureDiscipline.md): deadlines, budgets, subdivision, verification, classification, resumption.
|
|
61
|
+
- [The worker service](../../apps/worker/README.md): running it.
|
|
142
62
|
|
|
143
63
|
## License
|
|
144
64
|
|
|
145
65
|
Apache-2.0
|
|
146
|
-
|
|
147
|
-
## Related Packages
|
|
148
|
-
|
|
149
|
-
- [`@semiont/core`](../core/) — Domain types, `SemiontProject`, EventBus, `BusRequestPrimitive`
|
|
150
|
-
- [`@semiont/sdk`](../sdk/) — `SemiontSession`, `SemiontClient`
|
|
151
|
-
- [`@semiont/http-transport`](../http-transport/) — HTTP transport, OpenAPI types
|
|
152
|
-
- [`@semiont/inference`](../inference/) — AI inference client
|
|
153
|
-
- [`@semiont/make-meaning`](../make-meaning/) — Actor model, Knowledge Base, service orchestration
|