@cogitator-ai/worker 0.9.24 → 0.11.0

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 CHANGED
@@ -8,7 +8,7 @@ Distributed job queue for Cogitator agent execution. Built on BullMQ for reliabl
8
8
  pnpm add @cogitator-ai/worker @cogitator-ai/core ioredis
9
9
  ```
10
10
 
11
- BullMQ and `@cogitator-ai/swarms` come as dependencies; `ioredis` is a peer dependency. Website docs: [Worker Queues](https://cogitator.app/docs/deployment/worker-queues), [Distributed Swarms](https://cogitator.app/docs/swarms/distributed).
11
+ BullMQ, `@cogitator-ai/swarms` and `@cogitator-ai/workflows` come as dependencies, `ioredis` is a peer dependency. Website docs: [Worker Queues](https://cogitator.app/docs/deployment/worker-queues), [Distributed Swarms](https://cogitator.app/docs/swarms/distributed).
12
12
 
13
13
  ## Features
14
14
 
@@ -16,7 +16,9 @@ BullMQ and `@cogitator-ai/swarms` come as dependencies; `ioredis` is a peer depe
16
16
  - **Job Types** - Agents, workflow graphs, swarms and distributed swarm turns
17
17
  - **Worker Runtime** - Run jobs with your own `Cogitator` (provider keys, memory) and tool implementations
18
18
  - **Distributed Swarms** - `DistributedSwarmWorker` executes agent turns for `@cogitator-ai/swarms`
19
- - **Auto-Retry** - Exponential backoff for failed jobs
19
+ - **Auto-Retry** - Exponential backoff for failed jobs, a retried workflow job goes on from its last checkpoint
20
+ - **Job Ids** - Adding a job under an id the queue holds gives that job back, so producers can retry an add
21
+ - **Usage** - Workflow and swarm results carry tokens and cost, the metrics count them by job type
20
22
  - **Priority Queue** - Process important jobs first
21
23
  - **Delayed Jobs** - Schedule jobs for later execution
22
24
  - **Prometheus Metrics** - Built-in HPA support
@@ -129,6 +131,8 @@ interface QueueConfig {
129
131
  }
130
132
  ```
131
133
 
134
+ `redis` is the `RedisConnectionConfig` of `@cogitator-ai/types`, the same config `DistributedSwarmWorker` and distributed swarms take. `resolveRedisOptions(redis)` gives the settings it resolves to for one server.
135
+
132
136
  ### Adding Jobs
133
137
 
134
138
  **Agent Jobs:**
@@ -188,7 +192,7 @@ The worker routes `model` exactly like the same agent in-process: a prefix that
188
192
 
189
193
  **Workflow Jobs:**
190
194
 
191
- Workflow jobs run a DAG over a shared state object initialised from the job input. Nodes run as soon as their predecessors settle; independent branches run concurrently.
195
+ Workflow jobs run on the `WorkflowExecutor` of `@cogitator-ai/workflows`, over a shared state object initialised from the job input. Nodes run in rounds, each running at once every node whose nodes before it all settled, and a graph with many roots starts at all of them.
192
196
 
193
197
  ```typescript
194
198
  const workflowConfig: SerializedWorkflow = {
@@ -233,7 +237,9 @@ await queue.addWorkflowJob(workflowConfig, { ticket: 'App crashes on login' });
233
237
  | `condition` | `{ key, operator, value? }` — `equals`, `not-equals`, `contains`, `exists`, `gt`, `lt`; outgoing edges use `condition: 'true' \| 'false'` |
234
238
  | `parallel` | `{}` — fan-out marker; successors run concurrently |
235
239
 
236
- Node outputs are stored in the state under `outputKey` (default: node id). Nodes reachable only through untaken condition branches are skipped (`{ skipped: true }` in `nodeResults`). Graphs are validated before execution (unknown nodes, invalid configs, cycles).
240
+ Node outputs are stored in the state under `outputKey` (default: node id). Nodes reachable only through untaken condition branches are skipped (`{ skipped: true }` in `nodeResults`). Graphs are validated before execution (unknown nodes, invalid configs, cycles). The run keeps its own bookkeeping (which nodes ran, the branch a condition took, the usage of each agent node) in state keys starting with `__cogitator:`, which are left out of the job's `output`. Input keys, node ids and output keys starting with `__cogitator:` are refused.
241
+
242
+ A workflow job keeps one checkpoint, saved over after every node under the job's id and `runId`, so jobs sharing a `runId` never resume from each other. A job BullMQ retries (`attempts`) goes on from it instead of running its finished agent nodes again. The checkpoint is deleted once the run finished, and once no attempt follows a failure: after the last attempt, a cancelled job or a paused agent run. A job a shutdown stopped keeps it for the worker that takes the job up. `WorkerPool` keeps the checkpoints in its Redis under `<queue prefix>:<queue name>:workflow-checkpoints` (`cogitator`, or `{cogitator}` in cluster mode) for `checkpointTtl` seconds after the last save (7 days by default), so the checkpoint of a run whose worker crashed goes on its own. `checkpoints` in the worker config takes a `CheckpointStore` of your own.
237
243
 
238
244
  **Swarm Jobs:**
239
245
 
@@ -260,6 +266,18 @@ await queue.addSwarmJob(swarmConfig, 'Write an article about AI', {
260
266
  | `debate` | `debate` | `maxRounds` rounds, `coordinator` moderates |
261
267
  | `voting` | `consensus` | `consensusThreshold`, `maxRounds`, `coordinator` breaks ties |
262
268
 
269
+ **Job Ids:**
270
+
271
+ `addAgentJob`, `resumeAgentJob`, `addWorkflowJob`, `addSwarmJob` and `addSwarmAgentJob` take a `jobId`, and the job is added under it. Adding a job whose id the queue holds already gives that job back instead of adding a second one, so a producer can retry an add, such as after a timeout, without running the work twice:
272
+
273
+ ```typescript
274
+ const job = await queue.addWorkflowJob(workflowConfig, input, { jobId: `triage-${ticketId}` });
275
+ const again = await queue.addWorkflowJob(workflowConfig, input, { jobId: `triage-${ticketId}` });
276
+ // again.id === job.id, one job runs
277
+ ```
278
+
279
+ Two adds racing with the same id both get the job the queue holds. A job the queue removed (past `removeOnComplete` or `removeOnFail`) is gone, and its id adds a new job. BullMQ builds Redis keys from the id, so an empty id, one with `:`, an integer and a name of the queue's own keys (`meta`, `id`, `events`, `stalled-check` and the like) are refused, and an id the queue holds as another kind of job (an agent job for `addWorkflowJob`, say) throws. Without `jobId` every add gets a new id.
280
+
263
281
  ### Queue Methods
264
282
 
265
283
  ```typescript
@@ -335,6 +353,8 @@ interface WorkerConfig extends QueueConfig {
335
353
  stalledInterval?: number; // Default: 30000ms
336
354
  cogitator?: Cogitator; // Default: new Cogitator()
337
355
  tools?: Tool[]; // Tool implementations, resolved by name
356
+ checkpoints?: CheckpointStore; // Where workflow jobs keep their checkpoints. Default: the pool's Redis
357
+ checkpointTtl?: number; // Seconds the default store keeps a checkpoint after its last save. Default: 7 days
338
358
  }
339
359
  ```
340
360
 
@@ -391,7 +411,7 @@ Built-in processors handle each job type.
391
411
 
392
412
  ### Using Processors Directly
393
413
 
394
- Processors take the job payload and an optional runtime (`{ cogitator, tools }`):
414
+ Processors take the job payload and an optional runtime (`{ cogitator, tools, checkpoints }`):
395
415
 
396
416
  ```typescript
397
417
  import { processAgentJob, processWorkflowJob, processSwarmJob } from '@cogitator-ai/worker';
@@ -414,7 +434,9 @@ const swarmResult = await processSwarmJob(
414
434
  );
415
435
  ```
416
436
 
417
- `processSwarmAgentJob(payload, { publisher, isFinalAttempt, ...runtime })` executes one distributed swarm turn and publishes the result (tagged with the job id) to `payload.stateKeys.results`; `executeSwarmAgentJob` returns the result without publishing.
437
+ `processWorkflowJob` called on its own runs without checkpoints unless the runtime has `checkpoints` (a `CheckpointStore`). With them, pass `{ willRetry }` as the third argument: a run whose failure another attempt follows (`willRetry(error)` returns `true`) keeps its checkpoint for it, any other failure deletes it.
438
+
439
+ `processSwarmAgentJob(payload, { publisher, isFinalAttempt, ...runtime })` executes one distributed swarm turn and hands the result (tagged with the job id) to the coordinator with `deliverSwarmJobResult` of `@cogitator-ai/swarms`: when the job names `stateKeys.result` and the publisher has `set`, the result is kept there for `payload.resultTtl` ms (one hour by default), then it is published to `payload.stateKeys.results`. `publisher` is a `SwarmResultPublisher`, now an alias of `SwarmResultClient` (`publish`, optional `set`). `executeSwarmAgentJob` returns the result without publishing.
418
440
 
419
441
  ---
420
442
 
@@ -428,7 +450,7 @@ import { DistributedSwarmWorker } from '@cogitator-ai/worker';
428
450
 
429
451
  const worker = new DistributedSwarmWorker(
430
452
  {
431
- redis: { host: 'localhost', port: 6379 },
453
+ redis: { url: 'redis://localhost:6379' }, // the same Redis as the swarm's distributed.redis
432
454
  keyPrefix: 'swarm', // must match the swarm's distributed.redis.keyPrefix
433
455
  queue: 'swarm-agent-jobs', // must match distributed.queue
434
456
  concurrency: 4,
@@ -439,6 +461,7 @@ const worker = new DistributedSwarmWorker(
439
461
  onJobCompleted: (job) => console.log('done', job.agentName),
440
462
  onJobFailed: (job, error) => console.error(job.agentName, error.message),
441
463
  onJobCancelled: (job) => console.log('the swarm gave up on', job.jobId),
464
+ onJobsRecovered: (workerId, count) => console.warn(`${count} turns of ${workerId} requeued`),
442
465
  onError: (error) => console.error(error),
443
466
  }
444
467
  );
@@ -449,6 +472,18 @@ process.on('SIGTERM', () => void worker.stop()); // waits for in-flight turns
449
472
 
450
473
  Failed turns are reported back to the swarm as errors, so the swarm's own `errorHandling` (retry, failover, skip) applies. A turn the swarm gave up on (its run aborted or timed out, the swarm closed, another attempt answered) is skipped, or aborted while it runs (checked every `cancelCheckInterval` ms, default 1000), and reported to `onJobCancelled` instead of being published. Each turn runs on the model its agent would use in-process, routed by the worker's `cogitator`, so give the worker the same `llm.backends`, plugins and provider keys as the process that runs the swarm.
451
474
 
475
+ `redis` takes a `url` (`rediss://` for TLS), `host`, `port`, `username`, `password`, `db`, `tls` or `cluster.nodes`, like the queue. The turns sit in the list `<keyPrefix>:jobs:{<queue>}`, whose braces are a Redis Cluster hash tag that keeps the queue and its worker keys in one slot. Earlier versions used `<keyPrefix>:jobs:<queue>`, so upgrade the processes that run swarms and the workers together.
476
+
477
+ The queue is reliable:
478
+
479
+ - A worker moves each turn with `BLMOVE` (Redis 6.2 or newer) from the queue to its own list `<queue key>:processing:<workerId>`, and takes it off once the turn is done.
480
+ - Every `heartbeatInterval` ms (default 5000) it sets its heartbeat key `<queue key>:heartbeat:<workerId>`, which expires after three intervals, and stays in the set `<queue key>:workers`.
481
+ - On `start()` and on every beat, a worker moves the turns of workers whose heartbeat is gone back to the head of the queue, drops those workers from the set and reports `onJobsRecovered(workerId, count)`.
482
+ - A worker also checks its own list on every beat: a turn on it that the worker does not run at two beats in a row, such as one a `BLMOVE` moved while a dropped connection lost its reply, goes back to the head of the queue and is reported with the worker's own id.
483
+ - A worker silent for three intervals counts as gone, so a turn on a stalled worker can run twice.
484
+ - The result is kept under the job's `stateKeys.result` for `resultTtl` ms before it is published, so the coordinator finds it after a missed message, and a worker skips a job whose result is there (such as one a dead worker answered before it took the turn off its list). A result the worker cannot keep under its key is still published.
485
+ - `stop()` waits for in-flight turns, hands back what is left on its list, leaves the set and deletes its heartbeat. `getWorkerId()` gives the worker's id, new on every `start()`.
486
+
452
487
  ---
453
488
 
454
489
  ## Job Results
@@ -508,8 +543,17 @@ interface WorkflowJobResult {
508
543
  type: 'workflow';
509
544
  output: Record<string, unknown>;
510
545
  nodeResults: Record<string, unknown>;
546
+ usage: JobUsage; // what the agent nodes used and cost
511
547
  duration: number;
512
548
  }
549
+
550
+ interface JobUsage {
551
+ inputTokens?: number; // absent where the runs reported only their totals
552
+ outputTokens?: number;
553
+ totalTokens: number;
554
+ cost: number; // USD
555
+ runs: number; // agent runs counted
556
+ }
513
557
  ```
514
558
 
515
559
  ### Swarm Job Result
@@ -519,6 +563,8 @@ interface SwarmJobResult {
519
563
  type: 'swarm';
520
564
  output: string;
521
565
  rounds: number;
566
+ // what the swarm's agent runs used and cost, in all and by agent
567
+ usage: JobUsage & { byAgent: Record<string, { tokens: number; cost: number; runs: number }> };
522
568
  agentOutputs: {
523
569
  agent: string;
524
570
  output: string;
@@ -526,6 +572,8 @@ interface SwarmJobResult {
526
572
  }
527
573
  ```
528
574
 
575
+ A swarm reports only total tokens, so its `usage` has no `inputTokens` or `outputTokens`.
576
+
529
577
  ---
530
578
 
531
579
  ## Prometheus Metrics
@@ -554,20 +602,24 @@ app.listen(9090);
554
602
 
555
603
  ### Available Metrics
556
604
 
557
- | Metric | Type | Description |
558
- | -------------------------------- | --------- | ----------------------------------------------------------- |
559
- | `cogitator_queue_depth` | gauge | Total waiting + delayed jobs |
560
- | `cogitator_queue_waiting` | gauge | Jobs ready to run, prioritized jobs included |
561
- | `cogitator_queue_active` | gauge | Jobs currently being processed |
562
- | `cogitator_queue_completed` | gauge | Completed jobs kept in Redis (capped by `removeOnComplete`) |
563
- | `cogitator_queue_failed` | gauge | Failed jobs kept in Redis (capped by `removeOnFail`) |
564
- | `cogitator_queue_delayed` | gauge | Scheduled/delayed jobs |
565
- | `cogitator_workers_total` | gauge | Workers connected to the queue |
566
- | `cogitator_job_duration_seconds` | histogram | Job processing time |
567
- | `cogitator_jobs_by_type_total` | counter | Jobs by type |
568
- | `cogitator_jobs_failed_total` | counter | Jobs that failed their last attempt, by `type` |
569
-
570
- `cogitator_queue_completed` and `cogitator_queue_failed` can go down as BullMQ trims old jobs, so alert on `cogitator_jobs_failed_total` instead (for example `increase(cogitator_jobs_failed_total[5m]) > 5`). The per-type counters appear after the first job of that kind.
605
+ | Metric | Type | Description |
606
+ | ----------------------------------- | --------- | ----------------------------------------------------------- |
607
+ | `cogitator_queue_depth` | gauge | Total waiting + delayed jobs |
608
+ | `cogitator_queue_waiting` | gauge | Jobs ready to run, prioritized jobs included |
609
+ | `cogitator_queue_active` | gauge | Jobs currently being processed |
610
+ | `cogitator_queue_completed` | gauge | Completed jobs kept in Redis (capped by `removeOnComplete`) |
611
+ | `cogitator_queue_failed` | gauge | Failed jobs kept in Redis (capped by `removeOnFail`) |
612
+ | `cogitator_queue_delayed` | gauge | Scheduled/delayed jobs |
613
+ | `cogitator_workers_total` | gauge | Workers connected to the queue |
614
+ | `cogitator_job_duration_seconds` | histogram | Job processing time |
615
+ | `cogitator_jobs_by_type_total` | counter | Jobs by type |
616
+ | `cogitator_jobs_failed_total` | counter | Jobs that failed their last attempt, by `type` |
617
+ | `cogitator_job_tokens_total` | counter | Tokens the agent runs of completed jobs used, by `type` |
618
+ | `cogitator_job_input_tokens_total` | counter | Input tokens of completed jobs, by `type` |
619
+ | `cogitator_job_output_tokens_total` | counter | Output tokens of completed jobs, by `type` |
620
+ | `cogitator_job_cost_usd_total` | counter | USD the agent runs of completed jobs cost, by `type` |
621
+
622
+ `cogitator_queue_completed` and `cogitator_queue_failed` can go down as BullMQ trims old jobs, so alert on `cogitator_jobs_failed_total` instead (for example `increase(cogitator_jobs_failed_total[5m]) > 5`). The per-type counters appear after the first job of that kind. The usage counters take the `usage` of completed agent, workflow, swarm and swarm-agent jobs, input and output tokens only where the job reported them apart (swarm jobs do not).
571
623
 
572
624
  ### Duration Histogram
573
625
 
@@ -587,7 +639,7 @@ histogram.reset();
587
639
 
588
640
  ### Metrics Collector
589
641
 
590
- `WorkerPool` keeps one in `pool.metrics`: it calls `recordJob(type, durationMs)` for each completed job and `recordFailure(type)` for each job that failed its last attempt (retried attempts are not counted). Use your own collector when you process jobs outside the pool.
642
+ `WorkerPool` keeps one in `pool.metrics`: it calls `recordJob(type, durationMs)` and `recordUsage(type, result.usage)` for each completed job and `recordFailure(type)` for each job that failed its last attempt (retried attempts are not counted). Use your own collector when you process jobs outside the pool.
591
643
 
592
644
  ```typescript
593
645
  import { MetricsCollector } from '@cogitator-ai/worker';
@@ -596,6 +648,7 @@ const collector = new MetricsCollector();
596
648
 
597
649
  collector.recordJob('agent', 1500);
598
650
  collector.recordJob('workflow', 3200);
651
+ collector.recordUsage('workflow', workflowResult.usage); // { inputTokens?, outputTokens?, totalTokens, cost }
599
652
  collector.recordFailure('agent');
600
653
 
601
654
  const output = collector.format(queueMetrics, { queue: 'main' });
@@ -885,6 +938,7 @@ import type {
885
938
  AgentJobResult,
886
939
  WorkflowJobResult,
887
940
  SwarmJobResult,
941
+ JobUsage,
888
942
  SwarmAgentJobResult,
889
943
 
890
944
  // Configuration
@@ -1,23 +1,8 @@
1
1
  import { Cluster, Redis } from 'ioredis';
2
2
  import type { ConnectionOptions } from 'bullmq';
3
3
  import type { QueueConfig } from './types.js';
4
+ export { resolveRedisOptions, type RedisConnectionOptions } from '@cogitator-ai/swarms';
4
5
  export declare const DEFAULT_QUEUE_NAME = "cogitator-jobs";
5
- /** A single Redis server's connection settings, the url read and the explicit fields applied. */
6
- export interface RedisConnectionOptions {
7
- host: string;
8
- port: number;
9
- username?: string;
10
- password?: string;
11
- db?: number;
12
- tls?: Record<string, never>;
13
- }
14
- /**
15
- * Connection settings from the queue's redis config: `url` first (`redis://` or `rediss://` for
16
- * TLS, `user:password@`, a `/db` path), then the explicit fields, which win.
17
- *
18
- * @throws Error for a url that is not redis:// or rediss://, or a db path that is not a number
19
- */
20
- export declare function resolveRedisOptions(redis: RedisConnectionConfig): RedisConnectionOptions;
21
6
  export type RedisClient = Redis | Cluster;
22
7
  type RedisConnectionConfig = QueueConfig['redis'];
23
8
  /**
@@ -41,5 +26,4 @@ export declare function createBullConnection(redis: RedisConnectionConfig, optio
41
26
  * Plain client (e.g. for publishing swarm results) using the same Redis configuration.
42
27
  */
43
28
  export declare function createRedisClient(redis: RedisConnectionConfig): RedisClient;
44
- export {};
45
29
  //# sourceMappingURL=connection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AAEnD,iGAAiG;AACjG,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAgCxF;AAED,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,CAAC;AAE1C,KAAK,qBAAqB,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AAElD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAEhE;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,yEAAyE;IACzE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAaD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,qBAAqB,EAC5B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GACnC,cAAc,CA4BhB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,qBAAqB,GAAG,WAAW,CAS3E"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,OAAO,EAAE,mBAAmB,EAAE,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAExF,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AAEnD,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,CAAC;AAE1C,KAAK,qBAAqB,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AAElD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAEhE;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,yEAAyE;IACzE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,qBAAqB,EAC5B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GACnC,cAAc,CA4BhB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,qBAAqB,GAAG,WAAW,CAS3E"}
@@ -1,58 +1,13 @@
1
1
  import { Cluster, Redis } from 'ioredis';
2
+ import { clusterNodeOptions, resolveRedisOptions } from '@cogitator-ai/swarms';
3
+ export { resolveRedisOptions } from '@cogitator-ai/swarms';
2
4
  export const DEFAULT_QUEUE_NAME = 'cogitator-jobs';
3
- /**
4
- * Connection settings from the queue's redis config: `url` first (`redis://` or `rediss://` for
5
- * TLS, `user:password@`, a `/db` path), then the explicit fields, which win.
6
- *
7
- * @throws Error for a url that is not redis:// or rediss://, or a db path that is not a number
8
- */
9
- export function resolveRedisOptions(redis) {
10
- let fromUrl = {};
11
- if (redis.url) {
12
- const url = new URL(redis.url);
13
- if (url.protocol !== 'redis:' && url.protocol !== 'rediss:') {
14
- throw new Error(`Redis url must start with redis:// or rediss://, got ${url.protocol}//`);
15
- }
16
- const dbPath = url.pathname.replace(/^\//, '');
17
- if (dbPath !== '' && !/^\d+$/.test(dbPath)) {
18
- throw new Error(`Redis url database must be a number, got "${dbPath}"`);
19
- }
20
- fromUrl = {
21
- ...(url.hostname && { host: url.hostname }),
22
- ...(url.port && { port: Number(url.port) }),
23
- ...(url.username && { username: decodeURIComponent(url.username) }),
24
- ...(url.password && { password: decodeURIComponent(url.password) }),
25
- ...(dbPath !== '' && { db: Number(dbPath) }),
26
- tlsFromUrl: url.protocol === 'rediss:',
27
- };
28
- }
29
- const tls = redis.tls ?? fromUrl.tlsFromUrl ?? false;
30
- const username = redis.username ?? fromUrl.username;
31
- const password = redis.password ?? fromUrl.password;
32
- const db = redis.db ?? fromUrl.db;
33
- return {
34
- host: redis.host ?? fromUrl.host ?? 'localhost',
35
- port: redis.port ?? fromUrl.port ?? 6379,
36
- ...(username !== undefined && { username }),
37
- ...(password !== undefined && { password }),
38
- ...(db !== undefined && { db }),
39
- ...(tls && { tls: {} }),
40
- };
41
- }
42
5
  /**
43
6
  * BullMQ key prefix; cluster mode needs a hash tag so all queue keys share a slot.
44
7
  */
45
8
  export function queuePrefix(redis) {
46
9
  return redis.cluster ? '{cogitator}' : 'cogitator';
47
10
  }
48
- /**
49
- * What every cluster node connection needs from the config: the credentials and TLS of the url
50
- * and the explicit fields, without a host, port or database (a cluster has no database).
51
- */
52
- function clusterNodeOptions(redis) {
53
- const { host: _host, port: _port, db: _db, ...rest } = resolveRedisOptions(redis);
54
- return rest;
55
- }
56
11
  /**
57
12
  * Connection for BullMQ queues and workers. Blocking (worker) connections must not limit
58
13
  * retries per request, as BullMQ requires. Cluster instances are shared with BullMQ, which
@@ -1 +1 @@
1
- {"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAG/E,OAAO,EAAE,mBAAmB,EAA+B,MAAM,sBAAsB,CAAC;AAExF,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AAMnD;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,KAA4B;IACtD,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC;AACrD,CAAC;AAQD;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAA4B,EAC5B,UAAkC,EAAE;IAEpC,MAAM,oBAAoB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IAEjE,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5E,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE;YAC/C,WAAW,EAAE,IAAI;YACjB,YAAY,EAAE,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,EAAE,oBAAoB,EAAE;SACrE,CAAC,CAAC;QACH,OAAO;YACL,UAAU,EAAE,OAAO;YACnB,OAAO,EAAE,KAAK,IAAI,EAAE;gBAClB,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK;oBAAE,OAAO;gBACrC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;oBAC9B,OAAO,CAAC,UAAU,EAAE,CAAC;oBACrB,OAAO;gBACT,CAAC;gBACD,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;YACvB,CAAC;SACF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,UAAU,EAAE,EAAE,GAAG,mBAAmB,CAAC,KAAK,CAAC,EAAE,oBAAoB,EAAE;QACnE,OAAO,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;KACxB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAA4B;IAC5D,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,YAAY,EAAE,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC;IAED,OAAO,IAAI,KAAK,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/C,CAAC"}
@@ -2,18 +2,18 @@
2
2
  * Worker node for distributed swarms.
3
3
  *
4
4
  * Consumes agent turns dispatched by `DistributedSwarmCoordinator` (from
5
- * `@cogitator-ai/swarms`), runs them with the worker's Cogitator and tools, and publishes
6
- * each result back to the coordinator's results channel.
5
+ * `@cogitator-ai/swarms`), runs them with the worker's Cogitator and tools, and hands each
6
+ * result back to the coordinator. A turn moves from the queue to this worker's own list while
7
+ * it runs, so a turn of a worker that died goes back to the queue for another one.
7
8
  */
9
+ import type { RedisConnectionConfig } from '@cogitator-ai/types';
8
10
  import type { SwarmAgentJobPayload, SwarmAgentJobResult, WorkerRuntime } from './types.js';
9
11
  export interface DistributedSwarmWorkerConfig extends WorkerRuntime {
10
- /** Redis holding the swarm job queue (must match the swarm's `distributed.redis`) */
11
- redis?: {
12
- host?: string;
13
- port?: number;
14
- password?: string;
15
- db?: number;
16
- };
12
+ /**
13
+ * Redis holding the swarm job queue (must match the swarm's `distributed.redis`): a url, host
14
+ * and port, TLS, or a cluster. Default: localhost:6379
15
+ */
16
+ redis?: RedisConnectionConfig;
17
17
  /** Swarm key prefix (must match `distributed.redis.keyPrefix`, default: 'swarm') */
18
18
  keyPrefix?: string;
19
19
  /** Job queue name (must match `distributed.queue`, default: 'swarm-agent-jobs') */
@@ -28,39 +28,98 @@ export interface DistributedSwarmWorkerConfig extends WorkerRuntime {
28
28
  * cancelled turn is aborted and its result is not published
29
29
  */
30
30
  cancelCheckInterval?: number;
31
+ /**
32
+ * How often, in ms, the worker tells the queue it is alive and returns the turns of workers
33
+ * that stopped doing so to the queue (default: 5000). A worker silent for three intervals
34
+ * counts as gone, so a turn that runs past that on a stalled worker can run twice
35
+ */
36
+ heartbeatInterval?: number;
31
37
  }
32
38
  export interface DistributedSwarmWorkerEvents {
33
39
  onJobStarted?: (job: SwarmAgentJobPayload) => void;
34
40
  onJobCompleted?: (job: SwarmAgentJobPayload, result: SwarmAgentJobResult) => void;
35
41
  onJobFailed?: (job: SwarmAgentJobPayload, error: Error) => void;
36
- /** The coordinator gave up on the turn: it was skipped, or aborted while it ran */
42
+ /**
43
+ * The coordinator gave up on the turn or already has its result: it was skipped, or aborted
44
+ * while it ran
45
+ */
37
46
  onJobCancelled?: (job: SwarmAgentJobPayload) => void;
47
+ /**
48
+ * Turns went back to the queue: those of a worker that stopped beating, or ones on this
49
+ * worker's own list it never got (`workerId` is then its own)
50
+ */
51
+ onJobsRecovered?: (workerId: string, count: number) => void;
38
52
  onError?: (error: Error) => void;
39
53
  }
40
54
  export declare class DistributedSwarmWorker {
41
55
  private readonly config;
42
56
  private readonly events;
43
57
  private readonly runtime;
44
- private readonly queueKey;
58
+ private readonly keys;
59
+ /** This worker's id in the queue's worker set, new for every start */
60
+ private workerId;
45
61
  private consumer?;
46
62
  private publisher?;
47
63
  private loop?;
64
+ /** The next beat, scheduled once the one before it finished */
65
+ private beat?;
66
+ /** The beat running now, which `stop()` waits for */
67
+ private beating?;
68
+ /** Whether the worker beats: from `start()` until `stop()` saw its last turn finish */
69
+ private alive;
48
70
  private running;
49
71
  private readonly active;
72
+ /** The turns this worker took and runs, as moved: one entry per copy */
73
+ private readonly holding;
74
+ /** Turns on this worker's list it did not run at the last beat, with how many copies */
75
+ private strays;
50
76
  constructor(config?: DistributedSwarmWorkerConfig, events?: DistributedSwarmWorkerEvents);
51
77
  /**
52
- * Start consuming jobs. Resolves once the worker is connected.
78
+ * Start consuming jobs. Resolves once the worker is connected, has joined the queue's workers
79
+ * and has returned the turns of workers that are gone to the queue.
53
80
  */
54
81
  start(): Promise<void>;
55
82
  /**
56
- * Stop taking new jobs, wait for in-flight jobs to finish and close connections.
83
+ * Stop taking new jobs, wait for in-flight jobs to finish, hand back to the queue whatever is
84
+ * still on this worker's list, leave the queue's workers and close connections.
57
85
  */
58
86
  stop(): Promise<void>;
87
+ /** This worker's id in the queue's worker set while it runs */
88
+ getWorkerId(): string;
59
89
  isRunning(): boolean;
60
90
  getActiveJobCount(): number;
61
91
  private consume;
92
+ /**
93
+ * Beat once `heartbeatInterval` ms from now, and again that long after it finished: one beat at
94
+ * a time, so a slow Redis never runs two `reclaim()` at once or shortens the grace between two
95
+ */
96
+ private scheduleBeat;
97
+ private heartbeatInterval;
98
+ /** Tell the queue this worker is alive: its heartbeat first, so no one takes it for gone */
99
+ private heartbeat;
100
+ /**
101
+ * Hand back to the queue the turns on this worker's own list it does not run, at the second
102
+ * beat in a row that finds them there: a BLMOVE whose reply a dropped connection lost (and
103
+ * ioredis sent again) moved a turn this worker never got. One beat of grace lets a reply that
104
+ * is still on its way arrive first
105
+ */
106
+ private reclaim;
107
+ /** Return the turns of every worker whose heartbeat is gone to the queue */
108
+ private recover;
109
+ /**
110
+ * Move the turns on a worker's list back to the head of the queue, one at a time so two
111
+ * workers recovering it at once never move a turn twice
112
+ *
113
+ * @returns how many turns this call moved
114
+ */
115
+ private requeue;
116
+ private reportError;
62
117
  private handle;
63
- /** Whether the coordinator gave up on the job (only coordinators that name a cancel set). */
118
+ /**
119
+ * Whether the coordinator gave up on the job (only coordinators that name a cancel set), or a
120
+ * worker answered it already, such as one that died after answering and before taking the job
121
+ * off its list
122
+ */
64
123
  private isCancelled;
65
124
  /** Abort `controller` once the coordinator cancels the job while it runs. */
66
125
  private watchCancellation;
@@ -1 +1 @@
1
- {"version":3,"file":"distributed-swarm-worker.d.ts","sourceRoot":"","sources":["../src/distributed-swarm-worker.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAKxF,MAAM,WAAW,4BAA6B,SAAQ,aAAa;IACjE,qFAAqF;IACrF,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,CAAC;IACF,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,4BAA4B;IAC3C,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACnD,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,oBAAoB,EAAE,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAClF,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,oBAAoB,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAChE,mFAAmF;IACnF,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACrD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAgBD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAClD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,CAAQ;IACzB,OAAO,CAAC,SAAS,CAAC,CAAQ;IAC1B,OAAO,CAAC,IAAI,CAAC,CAAgB;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;gBAGjD,MAAM,GAAE,4BAAiC,EACzC,MAAM,GAAE,4BAAiC;IAW3C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B,SAAS,IAAI,OAAO;IAIpB,iBAAiB,IAAI,MAAM;YAIb,OAAO;YA6BP,MAAM;IAiDpB,6FAA6F;YAC/E,WAAW;IAWzB,6EAA6E;IAC7E,OAAO,CAAC,iBAAiB;CAqB1B"}
1
+ {"version":3,"file":"distributed-swarm-worker.d.ts","sourceRoot":"","sources":["../src/distributed-swarm-worker.ts"],"names":[],"mappings":"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"}