@lota-sdk/core 0.4.8 → 0.4.10
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/package.json +11 -12
- package/src/ai/embedding-cache.ts +96 -22
- package/src/ai-gateway/ai-gateway.ts +766 -223
- package/src/config/agent-defaults.ts +189 -75
- package/src/config/agent-types.ts +54 -4
- package/src/config/background-processing.ts +1 -1
- package/src/config/constants.ts +8 -2
- package/src/config/index.ts +0 -1
- package/src/config/logger.ts +299 -19
- package/src/config/thread-defaults.ts +40 -20
- package/src/create-runtime.ts +200 -449
- package/src/db/base.service.ts +52 -28
- package/src/db/cursor-pagination.ts +71 -30
- package/src/db/memory-query-builder.ts +2 -1
- package/src/db/memory-store.helpers.ts +4 -7
- package/src/db/memory-store.ts +868 -601
- package/src/db/memory.ts +396 -280
- package/src/db/record-id.ts +32 -10
- package/src/db/schema-fingerprint.ts +30 -12
- package/src/db/service-normalization.ts +288 -0
- package/src/db/service.ts +912 -779
- package/src/db/startup.ts +153 -68
- package/src/db/transaction-conflict.ts +15 -0
- package/src/effect/awaitable-effect.ts +96 -0
- package/src/effect/errors.ts +121 -0
- package/src/effect/helpers.ts +123 -0
- package/src/effect/index.ts +24 -0
- package/src/effect/layers.ts +238 -0
- package/src/effect/runtime-ref.ts +25 -0
- package/src/effect/runtime.ts +46 -0
- package/src/effect/services.ts +61 -0
- package/src/effect/zod.ts +43 -0
- package/src/embeddings/provider.ts +128 -83
- package/src/index.ts +48 -1
- package/src/openrouter/direct-provider.ts +11 -35
- package/src/queues/autonomous-job.queue.ts +117 -73
- package/src/queues/context-compaction.queue.ts +50 -17
- package/src/queues/delayed-node-promotion.queue.ts +46 -17
- package/src/queues/document-processor.queue.ts +52 -77
- package/src/queues/memory-consolidation.queue.ts +47 -32
- package/src/queues/organization-learning.queue.ts +26 -4
- package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
- package/src/queues/plan-scheduler.queue.ts +97 -33
- package/src/queues/post-chat-memory.queue.ts +56 -26
- package/src/queues/queue-factory.ts +227 -59
- package/src/queues/standalone-worker.ts +39 -0
- package/src/queues/title-generation.queue.ts +45 -11
- package/src/redis/connection.ts +182 -113
- package/src/redis/index.ts +6 -8
- package/src/redis/org-memory-lock.ts +60 -27
- package/src/redis/redis-lease-lock.ts +200 -121
- package/src/redis/runtime-connection.ts +20 -0
- package/src/redis/stream-context.ts +92 -46
- package/src/runtime/agent-identity-overrides.ts +2 -2
- package/src/runtime/agent-runtime-policy.ts +5 -2
- package/src/runtime/agent-stream-helpers.ts +24 -9
- package/src/runtime/chat-run-orchestration.ts +102 -19
- package/src/runtime/chat-run-registry.ts +36 -2
- package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
- package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
- package/src/runtime/domain-layer.ts +192 -0
- package/src/runtime/execution-plan-visibility.ts +2 -2
- package/src/runtime/execution-plan.ts +42 -15
- package/src/runtime/graph-designer.ts +16 -4
- package/src/runtime/helper-model.ts +139 -48
- package/src/runtime/index.ts +7 -8
- package/src/runtime/indexed-repositories-policy.ts +3 -3
- package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
- package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
- package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
- package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
- package/src/runtime/memory/memory-scope.ts +53 -0
- package/src/runtime/plugin-resolution.ts +124 -25
- package/src/runtime/plugin-types.ts +9 -1
- package/src/runtime/post-turn-side-effects.ts +177 -130
- package/src/runtime/retrieval-adapters.ts +40 -6
- package/src/runtime/runtime-accessors.ts +92 -0
- package/src/runtime/runtime-config.ts +150 -61
- package/src/runtime/runtime-extensions.ts +23 -25
- package/src/runtime/runtime-lifecycle.ts +124 -0
- package/src/runtime/runtime-services.ts +386 -0
- package/src/runtime/runtime-token.ts +47 -0
- package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
- package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
- package/src/runtime/social-chat/social-chat.ts +630 -0
- package/src/runtime/specialist-runner.ts +36 -10
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
- package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
- package/src/runtime/thread-chat-helpers.ts +2 -2
- package/src/runtime/thread-plan-turn.ts +2 -1
- package/src/runtime/thread-turn-context.ts +183 -111
- package/src/runtime/turn-lifecycle.ts +93 -27
- package/src/services/agent-activity.service.ts +287 -203
- package/src/services/agent-executor.service.ts +253 -149
- package/src/services/artifact.service.ts +231 -149
- package/src/services/attachment.service.ts +171 -115
- package/src/services/autonomous-job.service.ts +890 -491
- package/src/services/background-work.service.ts +54 -0
- package/src/services/chat-run-registry.service.ts +13 -1
- package/src/services/context-compaction.service.ts +136 -86
- package/src/services/document-chunk.service.ts +151 -88
- package/src/services/execution-plan/execution-plan-approval.ts +26 -0
- package/src/services/execution-plan/execution-plan-context.ts +29 -0
- package/src/services/execution-plan/execution-plan-graph.ts +278 -0
- package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
- package/src/services/execution-plan/execution-plan-spec.ts +75 -0
- package/src/services/execution-plan/execution-plan.service.ts +1041 -0
- package/src/services/feedback-loop.service.ts +132 -76
- package/src/services/global-orchestrator.service.ts +101 -168
- package/src/services/graph-full-routing.ts +193 -0
- package/src/services/index.ts +19 -21
- package/src/services/institutional-memory.service.ts +213 -125
- package/src/services/learned-skill.service.ts +368 -260
- package/src/services/memory/memory-conversation.ts +95 -0
- package/src/services/memory/memory-errors.ts +27 -0
- package/src/services/memory/memory-org-memory.ts +50 -0
- package/src/services/memory/memory-preseeded.ts +86 -0
- package/src/services/memory/memory-rerank.ts +297 -0
- package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
- package/src/services/memory/memory.service.ts +674 -0
- package/src/services/memory/rerank.service.ts +201 -0
- package/src/services/monitoring-window.service.ts +92 -70
- package/src/services/mutating-approval.service.ts +62 -53
- package/src/services/node-workspace.service.ts +141 -98
- package/src/services/notification.service.ts +29 -16
- package/src/services/organization-member.service.ts +120 -66
- package/src/services/organization.service.ts +153 -77
- package/src/services/ownership-dispatcher.service.ts +456 -263
- package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
- package/src/services/plan/plan-agent-query.service.ts +322 -0
- package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
- package/src/services/plan/plan-artifact.service.ts +60 -0
- package/src/services/plan/plan-builder.service.ts +76 -0
- package/src/services/plan/plan-checkpoint.service.ts +103 -0
- package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
- package/src/services/plan/plan-completion-side-effects.ts +169 -0
- package/src/services/plan/plan-coordination.service.ts +181 -0
- package/src/services/plan/plan-cycle.service.ts +405 -0
- package/src/services/plan/plan-deadline.service.ts +533 -0
- package/src/services/plan/plan-event-delivery.service.ts +266 -0
- package/src/services/plan/plan-executor-context.ts +35 -0
- package/src/services/plan/plan-executor-graph.ts +522 -0
- package/src/services/plan/plan-executor-helpers.ts +307 -0
- package/src/services/plan/plan-executor-persistence.ts +209 -0
- package/src/services/plan/plan-executor.service.ts +1737 -0
- package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
- package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
- package/src/services/plan/plan-run-serialization.ts +15 -0
- package/src/services/plan/plan-run.service.ts +637 -0
- package/src/services/plan/plan-scheduler.service.ts +379 -0
- package/src/services/plan/plan-template.service.ts +224 -0
- package/src/services/plan/plan-transaction-events.ts +36 -0
- package/src/services/plan/plan-validator.service.ts +907 -0
- package/src/services/plan/plan-workspace.service.ts +131 -0
- package/src/services/plugin-executor.service.ts +102 -68
- package/src/services/quality-metrics.service.ts +112 -94
- package/src/services/queue-job.service.ts +288 -231
- package/src/services/recent-activity-title.service.ts +73 -36
- package/src/services/recent-activity.service.ts +274 -259
- package/src/services/skill-resolver.service.ts +38 -12
- package/src/services/social-chat-history.service.ts +190 -122
- package/src/services/system-executor.service.ts +96 -61
- package/src/services/thread/thread-active-run.ts +203 -0
- package/src/services/thread/thread-bootstrap.ts +385 -0
- package/src/services/thread/thread-listing.ts +199 -0
- package/src/services/thread/thread-memory-block.ts +130 -0
- package/src/services/thread/thread-message.service.ts +379 -0
- package/src/services/thread/thread-record-store.ts +155 -0
- package/src/services/thread/thread-title.service.ts +74 -0
- package/src/services/thread/thread-turn-execution.ts +280 -0
- package/src/services/thread/thread-turn-message-context.ts +73 -0
- package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
- package/src/services/thread/thread-turn-streaming.ts +403 -0
- package/src/services/thread/thread-turn-tracing.ts +35 -0
- package/src/services/thread/thread-turn.ts +376 -0
- package/src/services/thread/thread.service.ts +344 -0
- package/src/services/user.service.ts +82 -32
- package/src/services/write-intent-validator.service.ts +63 -51
- package/src/storage/attachment-parser.ts +69 -27
- package/src/storage/attachment-storage.service.ts +334 -275
- package/src/storage/generated-document-storage.service.ts +66 -34
- package/src/system-agents/agent-result.ts +3 -1
- package/src/system-agents/context-compaction.agent.ts +3 -3
- package/src/system-agents/delegated-agent-factory.ts +159 -90
- package/src/system-agents/helper-agent-options.ts +1 -1
- package/src/system-agents/memory-reranker.agent.ts +3 -3
- package/src/system-agents/memory.agent.ts +3 -3
- package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
- package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
- package/src/system-agents/skill-extractor.agent.ts +3 -3
- package/src/system-agents/skill-manager.agent.ts +3 -3
- package/src/system-agents/thread-router.agent.ts +157 -113
- package/src/system-agents/title-generator.agent.ts +3 -3
- package/src/tools/execution-plan.tool.ts +241 -171
- package/src/tools/fetch-webpage.tool.ts +29 -18
- package/src/tools/firecrawl-client.ts +26 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/memory-block.tool.ts +14 -6
- package/src/tools/plan-approval.tool.ts +57 -47
- package/src/tools/read-file-parts.tool.ts +44 -33
- package/src/tools/remember-memory.tool.ts +65 -45
- package/src/tools/search-web.tool.ts +33 -22
- package/src/tools/search.tool.ts +41 -29
- package/src/tools/team-think.tool.ts +125 -84
- package/src/tools/user-questions.tool.ts +4 -3
- package/src/tools/web-tool-shared.ts +6 -0
- package/src/utils/async.ts +25 -22
- package/src/utils/crypto.ts +21 -0
- package/src/utils/date-time.ts +40 -1
- package/src/utils/errors.ts +111 -20
- package/src/utils/hono-error-handler.ts +24 -39
- package/src/utils/index.ts +2 -1
- package/src/utils/null-proto-record.ts +41 -0
- package/src/utils/sse-keepalive.ts +124 -21
- package/src/workers/bootstrap.ts +164 -52
- package/src/workers/memory-consolidation.worker.ts +325 -237
- package/src/workers/organization-learning.worker.ts +50 -16
- package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
- package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
- package/src/workers/skill-extraction.runner.ts +176 -93
- package/src/workers/utils/file-section-chunker.ts +8 -10
- package/src/workers/utils/repo-structure-extractor.ts +349 -260
- package/src/workers/utils/repomix-file-sections.ts +2 -2
- package/src/workers/utils/thread-message-query.ts +97 -38
- package/src/workers/worker-utils.ts +74 -31
- package/src/config/debug-logger.ts +0 -47
- package/src/config/search.ts +0 -3
- package/src/redis/connection-accessor.ts +0 -26
- package/src/runtime/agent-types.ts +0 -1
- package/src/runtime/context-compaction-runtime.ts +0 -87
- package/src/runtime/memory-scope.ts +0 -43
- package/src/runtime/social-chat-agent-runner.ts +0 -118
- package/src/runtime/social-chat.ts +0 -516
- package/src/runtime/team-consultation-orchestrator.ts +0 -272
- package/src/services/adaptive-playbook.service.ts +0 -152
- package/src/services/artifact-provenance.service.ts +0 -172
- package/src/services/chat-attachments.service.ts +0 -17
- package/src/services/context-compaction-runtime.singleton.ts +0 -13
- package/src/services/execution-plan.service.ts +0 -1118
- package/src/services/memory.service.ts +0 -914
- package/src/services/plan-agent-heartbeat.service.ts +0 -136
- package/src/services/plan-agent-query.service.ts +0 -267
- package/src/services/plan-artifact.service.ts +0 -50
- package/src/services/plan-builder.service.ts +0 -67
- package/src/services/plan-checkpoint.service.ts +0 -81
- package/src/services/plan-completion-side-effects.ts +0 -80
- package/src/services/plan-coordination.service.ts +0 -157
- package/src/services/plan-cycle.service.ts +0 -284
- package/src/services/plan-deadline.service.ts +0 -430
- package/src/services/plan-event-delivery.service.ts +0 -166
- package/src/services/plan-executor.service.ts +0 -1950
- package/src/services/plan-run.service.ts +0 -515
- package/src/services/plan-scheduler.service.ts +0 -240
- package/src/services/plan-template.service.ts +0 -177
- package/src/services/plan-validator.service.ts +0 -818
- package/src/services/plan-workspace.service.ts +0 -83
- package/src/services/rerank.service.ts +0 -156
- package/src/services/thread-message.service.ts +0 -275
- package/src/services/thread-plan-registry.service.ts +0 -22
- package/src/services/thread-title.service.ts +0 -39
- package/src/services/thread-turn-preparation.service.ts +0 -1147
- package/src/services/thread-turn.ts +0 -172
- package/src/services/thread.service.ts +0 -869
- package/src/utils/env.ts +0 -8
- /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
- /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
- /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
- /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
- /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
- /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
- /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
- /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import type { Job } from 'bullmq'
|
|
2
|
+
import { Schema, Effect } from 'effect'
|
|
3
|
+
import type { Context } from 'effect'
|
|
4
|
+
import type IORedis from 'ioredis'
|
|
2
5
|
|
|
3
6
|
import { serverLogger } from '../config/logger'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
+
import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
|
|
8
|
+
import { PlanCycleServiceTag } from '../services/plan/plan-cycle.service'
|
|
9
|
+
import { PlanDeadlineServiceTag } from '../services/plan/plan-deadline.service'
|
|
10
|
+
import { PlanExecutorServiceTag } from '../services/plan/plan-executor.service'
|
|
11
|
+
import { PlanSchedulerServiceTag } from '../services/plan/plan-scheduler.service'
|
|
12
|
+
import { nowEpochMillis } from '../utils/date-time'
|
|
7
13
|
import type { WorkerHandle } from '../workers/worker-utils'
|
|
8
|
-
import {
|
|
14
|
+
import { createQueueFactoryWithDeps } from './queue-factory'
|
|
15
|
+
import { runStandaloneQueueWorker } from './standalone-worker'
|
|
9
16
|
|
|
10
17
|
export interface PlanSchedulerFireJob {
|
|
11
18
|
type: 'fire-schedule'
|
|
@@ -21,50 +28,75 @@ export type PlanSchedulerJob = PlanSchedulerFireJob | PlanSchedulerDeadlineJob
|
|
|
21
28
|
|
|
22
29
|
export const PLAN_SCHEDULER_QUEUE = 'plan-scheduler'
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
interface PlanSchedulerQueueDeps {
|
|
32
|
+
databaseService: Context.Service.Shape<typeof DatabaseServiceTag>
|
|
33
|
+
planSchedulerService: Context.Service.Shape<typeof PlanSchedulerServiceTag>
|
|
34
|
+
planDeadlineService: Context.Service.Shape<typeof PlanDeadlineServiceTag>
|
|
35
|
+
planExecutorService: Context.Service.Shape<typeof PlanExecutorServiceTag>
|
|
36
|
+
planCycleService: Context.Service.Shape<typeof PlanCycleServiceTag>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class PlanSchedulerQueueError extends Schema.TaggedErrorClass<PlanSchedulerQueueError>()('PlanSchedulerQueueError', {
|
|
40
|
+
stage: Schema.Literals(['remove-schedule-fire-job', 'recover-active-schedules', 'recover-deadline-checks']),
|
|
41
|
+
message: Schema.String,
|
|
42
|
+
cause: Schema.Defect,
|
|
43
|
+
}) {}
|
|
44
|
+
|
|
45
|
+
function toPlanSchedulerQueueError(stage: PlanSchedulerQueueError['stage'], cause: unknown): PlanSchedulerQueueError {
|
|
46
|
+
return new PlanSchedulerQueueError({ stage, message: cause instanceof Error ? cause.message : String(cause), cause })
|
|
47
|
+
}
|
|
26
48
|
|
|
49
|
+
function processPlanSchedulerJob(deps: PlanSchedulerQueueDeps, job: Job<PlanSchedulerJob>): Promise<void> {
|
|
50
|
+
const { planSchedulerService, planDeadlineService, planExecutorService, planCycleService } = deps
|
|
27
51
|
switch (job.data.type) {
|
|
28
52
|
case 'fire-schedule':
|
|
29
|
-
|
|
30
|
-
|
|
53
|
+
return Effect.runPromise(
|
|
54
|
+
Effect.asVoid(
|
|
55
|
+
planSchedulerService.fireScheduleById(job.data.scheduleId, {
|
|
56
|
+
promoteDelayedNode: (params) => planExecutorService.promoteDelayedNode(params),
|
|
57
|
+
advanceCycle: (cycleId) => planCycleService.advanceCycle(cycleId),
|
|
58
|
+
recoverDeadlineChecks: () => Effect.runPromise(Effect.asVoid(planDeadlineService.recoverDeadlineChecks())),
|
|
59
|
+
}),
|
|
60
|
+
),
|
|
61
|
+
)
|
|
31
62
|
case 'check-deadlines':
|
|
32
|
-
|
|
33
|
-
break
|
|
63
|
+
return Effect.runPromise(Effect.asVoid(planDeadlineService.checkDeadlines()))
|
|
34
64
|
}
|
|
35
65
|
}
|
|
36
66
|
|
|
37
|
-
const planScheduler =
|
|
67
|
+
const planScheduler = createQueueFactoryWithDeps<PlanSchedulerJob, PlanSchedulerQueueDeps>({
|
|
38
68
|
name: PLAN_SCHEDULER_QUEUE,
|
|
39
69
|
displayName: 'Plan scheduler',
|
|
40
70
|
jobName: 'plan-scheduler-job',
|
|
41
71
|
concurrency: 1,
|
|
42
72
|
defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 5000 } },
|
|
73
|
+
prepare: ({ databaseService }) => databaseService.connect(),
|
|
43
74
|
processor: processPlanSchedulerJob,
|
|
44
75
|
})
|
|
45
76
|
|
|
46
77
|
/** Enqueue a delayed job that fires a specific schedule at its nextFireAt time. */
|
|
47
|
-
export
|
|
48
|
-
|
|
78
|
+
export function enqueueScheduleFire(scheduleId: string, delayMs: number): Promise<void> {
|
|
79
|
+
return planScheduler.enqueue(
|
|
49
80
|
{ type: 'fire-schedule', scheduleId },
|
|
50
81
|
{ delay: Math.max(0, delayMs), jobId: `schedule:${scheduleId}`, removeOnComplete: true, removeOnFail: 50 },
|
|
51
82
|
)
|
|
52
83
|
}
|
|
53
84
|
|
|
54
85
|
/** Remove a pending fire job for a schedule (on cancel/pause/complete). */
|
|
55
|
-
export
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
86
|
+
export function removeScheduleFireJob(scheduleId: string): Promise<void> {
|
|
87
|
+
return Effect.runPromise(
|
|
88
|
+
Effect.tryPromise({
|
|
89
|
+
try: () => planScheduler.getQueue().remove(`schedule:${scheduleId}`),
|
|
90
|
+
catch: (cause) => toPlanSchedulerQueueError('remove-schedule-fire-job', cause),
|
|
91
|
+
}).pipe(Effect.asVoid),
|
|
92
|
+
)
|
|
61
93
|
}
|
|
62
94
|
|
|
63
95
|
const DEADLINE_CHECK_JOB_PREFIX = 'deadline-check'
|
|
64
96
|
|
|
65
|
-
export
|
|
66
|
-
const delay = Math.max(0, scheduledFor.getTime() -
|
|
67
|
-
|
|
97
|
+
export function enqueueDeadlineCheck(scheduledFor: Date): Promise<void> {
|
|
98
|
+
const delay = Math.max(0, scheduledFor.getTime() - nowEpochMillis())
|
|
99
|
+
return planScheduler.enqueue(
|
|
68
100
|
{ type: 'check-deadlines', scheduledFor: scheduledFor.toISOString() },
|
|
69
101
|
{
|
|
70
102
|
delay,
|
|
@@ -75,23 +107,55 @@ export async function enqueueDeadlineCheck(scheduledFor: Date): Promise<void> {
|
|
|
75
107
|
)
|
|
76
108
|
}
|
|
77
109
|
|
|
78
|
-
export function startPlanSchedulerWorker(options: {
|
|
79
|
-
|
|
110
|
+
export function startPlanSchedulerWorker(options: {
|
|
111
|
+
registerSignals?: boolean
|
|
112
|
+
connectionProvider: () => IORedis
|
|
113
|
+
deps: PlanSchedulerQueueDeps
|
|
114
|
+
}): WorkerHandle {
|
|
115
|
+
const handle = planScheduler.startWorker({
|
|
116
|
+
deps: options.deps,
|
|
117
|
+
registerSignals: options.registerSignals,
|
|
118
|
+
connectionProvider: options.connectionProvider,
|
|
119
|
+
})
|
|
80
120
|
|
|
81
121
|
// Recover active schedules on startup
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
122
|
+
void Effect.runFork(
|
|
123
|
+
options.deps.planSchedulerService.recoverActiveSchedules().pipe(
|
|
124
|
+
Effect.mapError((cause) => toPlanSchedulerQueueError('recover-active-schedules', cause)),
|
|
125
|
+
Effect.catchTag('PlanSchedulerQueueError', (error) =>
|
|
126
|
+
Effect.sync(() => {
|
|
127
|
+
serverLogger.error`Plan scheduler startup recovery failed: ${error.message}`
|
|
128
|
+
}),
|
|
129
|
+
),
|
|
130
|
+
),
|
|
131
|
+
)
|
|
85
132
|
|
|
86
133
|
// Seed deadline checks from current runtime state. Subsequent checks are
|
|
87
134
|
// re-seeded by the queue job after each sweep and by schedule fire events.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
135
|
+
void Effect.runFork(
|
|
136
|
+
options.deps.planDeadlineService.recoverDeadlineChecks().pipe(
|
|
137
|
+
Effect.mapError((cause) => toPlanSchedulerQueueError('recover-deadline-checks', cause)),
|
|
138
|
+
Effect.catchTag('PlanSchedulerQueueError', (error) =>
|
|
139
|
+
Effect.sync(() => {
|
|
140
|
+
serverLogger.error`Plan deadline recovery failed: ${error.message}`
|
|
141
|
+
}),
|
|
142
|
+
),
|
|
143
|
+
),
|
|
144
|
+
)
|
|
91
145
|
|
|
92
146
|
return handle
|
|
93
147
|
}
|
|
94
148
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
149
|
+
runStandaloneQueueWorker((runtime) => {
|
|
150
|
+
const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
|
|
151
|
+
startPlanSchedulerWorker({
|
|
152
|
+
connectionProvider: () => resolve(RedisServiceTag).getConnectionForBullMQ(),
|
|
153
|
+
deps: {
|
|
154
|
+
databaseService: resolve(DatabaseServiceTag),
|
|
155
|
+
planSchedulerService: resolve(PlanSchedulerServiceTag),
|
|
156
|
+
planDeadlineService: resolve(PlanDeadlineServiceTag),
|
|
157
|
+
planExecutorService: resolve(PlanExecutorServiceTag),
|
|
158
|
+
planCycleService: resolve(PlanCycleServiceTag),
|
|
159
|
+
},
|
|
160
|
+
})
|
|
161
|
+
})
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
type OrganizationOnboardStatus = string
|
|
2
1
|
import type { Job } from 'bullmq'
|
|
2
|
+
import { Effect } from 'effect'
|
|
3
|
+
import type { Context } from 'effect'
|
|
4
|
+
import type IORedis from 'ioredis'
|
|
3
5
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import type { MaybeAwaitableService } from '../effect/awaitable-effect'
|
|
7
|
+
import { DatabaseServiceTag, RedisServiceTag } from '../effect/services'
|
|
8
|
+
import { MemoryServiceTag } from '../services/memory/memory.service'
|
|
9
|
+
import { createQueueFactoryWithDeps } from './queue-factory'
|
|
10
|
+
import { runStandaloneQueueWorker } from './standalone-worker'
|
|
7
11
|
|
|
8
12
|
interface PostChatMemoryMessage {
|
|
9
13
|
role: 'user' | 'agent'
|
|
@@ -17,7 +21,7 @@ interface PostChatMemoryExtractionJob {
|
|
|
17
21
|
sourceId: string
|
|
18
22
|
source?: string
|
|
19
23
|
sourceMetadata?: Record<string, unknown>
|
|
20
|
-
onboardStatus?:
|
|
24
|
+
onboardStatus?: string
|
|
21
25
|
userMessage: string
|
|
22
26
|
historyMessages: PostChatMemoryMessage[]
|
|
23
27
|
agentMessages: Array<{ content: string; agentName?: string }>
|
|
@@ -25,9 +29,13 @@ interface PostChatMemoryExtractionJob {
|
|
|
25
29
|
attachmentContext?: string
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
interface PostChatMemoryQueueDeps {
|
|
33
|
+
databaseService: Context.Service.Shape<typeof DatabaseServiceTag>
|
|
34
|
+
memoryService: MaybeAwaitableService<Context.Service.Shape<typeof MemoryServiceTag>>
|
|
35
|
+
}
|
|
30
36
|
|
|
37
|
+
function processPostChatMemoryJob(deps: PostChatMemoryQueueDeps, job: Job<PostChatMemoryExtractionJob>): Promise<void> {
|
|
38
|
+
const { memoryService } = deps
|
|
31
39
|
const data = job.data
|
|
32
40
|
const userMessage = data.userMessage.trim()
|
|
33
41
|
const agentMessages = data.agentMessages
|
|
@@ -37,7 +45,9 @@ async function processPostChatMemoryJob(job: Job<PostChatMemoryExtractionJob>):
|
|
|
37
45
|
}))
|
|
38
46
|
.filter((item) => item.content.length > 0)
|
|
39
47
|
|
|
40
|
-
if (!userMessage || agentMessages.length === 0)
|
|
48
|
+
if (!userMessage || agentMessages.length === 0) {
|
|
49
|
+
return Promise.resolve()
|
|
50
|
+
}
|
|
41
51
|
|
|
42
52
|
const joinedOutput = agentMessages
|
|
43
53
|
.map((item) => (item.agentName ? `[${item.agentName}] ${item.content}` : item.content))
|
|
@@ -51,23 +61,27 @@ async function processPostChatMemoryJob(job: Job<PostChatMemoryExtractionJob>):
|
|
|
51
61
|
),
|
|
52
62
|
]
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
return Effect.runPromise(
|
|
65
|
+
Effect.asVoid(
|
|
66
|
+
memoryService.addConversationMemories({
|
|
67
|
+
orgId: data.orgId,
|
|
68
|
+
input: userMessage,
|
|
69
|
+
output: joinedOutput,
|
|
70
|
+
sourceId: data.sourceId,
|
|
71
|
+
source: data.source,
|
|
72
|
+
sourceMetadata: data.sourceMetadata,
|
|
73
|
+
onboardStatus: data.onboardStatus,
|
|
74
|
+
...(uniqueAgentNames.length > 0 ? { agentName: uniqueAgentNames[0] } : {}),
|
|
75
|
+
historyMessages: data.historyMessages,
|
|
76
|
+
memoryBlock: data.memoryBlock,
|
|
77
|
+
attachmentContext: data.attachmentContext,
|
|
78
|
+
agentNames: uniqueAgentNames,
|
|
79
|
+
}),
|
|
80
|
+
),
|
|
81
|
+
)
|
|
68
82
|
}
|
|
69
83
|
|
|
70
|
-
const postChatMemory =
|
|
84
|
+
const postChatMemory = createQueueFactoryWithDeps<PostChatMemoryExtractionJob, PostChatMemoryQueueDeps>({
|
|
71
85
|
name: 'post-chat-memory',
|
|
72
86
|
displayName: 'Post-chat memory',
|
|
73
87
|
jobName: 'extract-memory',
|
|
@@ -76,14 +90,30 @@ const postChatMemory = createQueueFactory<PostChatMemoryExtractionJob>({
|
|
|
76
90
|
maxStalledCount: 10,
|
|
77
91
|
stalledInterval: 120_000,
|
|
78
92
|
defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 2_000 } },
|
|
93
|
+
prepare: ({ databaseService }) => databaseService.connect(),
|
|
79
94
|
processor: processPostChatMemoryJob,
|
|
80
95
|
})
|
|
81
96
|
|
|
82
97
|
export function enqueuePostChatMemory(job: PostChatMemoryExtractionJob, options?: { dedupeKey?: string }) {
|
|
83
98
|
return postChatMemory.enqueue(job, options?.dedupeKey ? { jobId: options.dedupeKey } : undefined)
|
|
84
99
|
}
|
|
85
|
-
export const startPostChatMemoryWorker = postChatMemory.startWorker
|
|
86
100
|
|
|
87
|
-
|
|
88
|
-
|
|
101
|
+
export function startPostChatMemoryWorker(options: {
|
|
102
|
+
registerSignals?: boolean
|
|
103
|
+
connectionProvider: () => IORedis
|
|
104
|
+
deps: PostChatMemoryQueueDeps
|
|
105
|
+
}) {
|
|
106
|
+
return postChatMemory.startWorker({
|
|
107
|
+
deps: options.deps,
|
|
108
|
+
registerSignals: options.registerSignals,
|
|
109
|
+
connectionProvider: options.connectionProvider,
|
|
110
|
+
})
|
|
89
111
|
}
|
|
112
|
+
|
|
113
|
+
runStandaloneQueueWorker((runtime) => {
|
|
114
|
+
const resolve = <I, T>(tag: Context.Key<I, T>): T => runtime.runSync(Effect.service(tag))
|
|
115
|
+
startPostChatMemoryWorker({
|
|
116
|
+
connectionProvider: () => resolve(RedisServiceTag).getConnectionForBullMQ(),
|
|
117
|
+
deps: { databaseService: resolve(DatabaseServiceTag), memoryService: resolve(MemoryServiceTag) },
|
|
118
|
+
})
|
|
119
|
+
})
|
|
@@ -1,24 +1,50 @@
|
|
|
1
1
|
import { Queue, Worker } from 'bullmq'
|
|
2
2
|
import type { Job, JobsOptions, QueueOptions, WorkerOptions } from 'bullmq'
|
|
3
|
+
import { Effect, Schema } from 'effect'
|
|
3
4
|
import type IORedis from 'ioredis'
|
|
4
5
|
|
|
6
|
+
import type { LotaLogger } from '../config/logger'
|
|
5
7
|
import { serverLogger } from '../config/logger'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
+
import { getCurrentRuntime } from '../effect/runtime-ref'
|
|
9
|
+
import { RedisServiceTag } from '../effect/services'
|
|
10
|
+
import type { TrackedBullJobLike } from '../services/queue-job.service'
|
|
8
11
|
import {
|
|
9
12
|
attachWorkerEvents,
|
|
10
13
|
createTracedWorkerProcessor,
|
|
11
14
|
createWorkerShutdown,
|
|
12
15
|
DEFAULT_JOB_RETENTION,
|
|
13
16
|
registerShutdownSignals,
|
|
17
|
+
getQueueJobService,
|
|
14
18
|
} from '../workers/worker-utils'
|
|
15
19
|
import type { WorkerHandle } from '../workers/worker-utils'
|
|
16
20
|
|
|
21
|
+
class QueueFactoryError extends Schema.TaggedErrorClass<QueueFactoryError>()('@lota-sdk/core/QueueFactoryError', {
|
|
22
|
+
message: Schema.String,
|
|
23
|
+
cause: Schema.optional(Schema.Defect),
|
|
24
|
+
}) {}
|
|
25
|
+
|
|
26
|
+
function getDefaultQueueConnectionProvider(): () => IORedis {
|
|
27
|
+
const redis = getCurrentRuntime().runSync(Effect.service(RedisServiceTag))
|
|
28
|
+
return () => redis.getConnectionForBullMQ()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type QueueShape<TJob> = Queue<TJob, unknown, string, TJob, unknown, string>
|
|
32
|
+
type QueueMethod = 'add' | 'close' | 'remove' | 'removeDeduplicationKey' | 'removeJobScheduler'
|
|
33
|
+
|
|
34
|
+
const queueMethodsThatWaitForClose = new Set<QueueMethod>([
|
|
35
|
+
'add',
|
|
36
|
+
'close',
|
|
37
|
+
'remove',
|
|
38
|
+
'removeDeduplicationKey',
|
|
39
|
+
'removeJobScheduler',
|
|
40
|
+
])
|
|
41
|
+
|
|
17
42
|
interface QueueFactoryConfigBase {
|
|
18
43
|
name: string
|
|
19
44
|
displayName: string
|
|
20
45
|
jobName: string
|
|
21
46
|
concurrency: number
|
|
47
|
+
logger?: LotaLogger
|
|
22
48
|
lockDuration?: number
|
|
23
49
|
stalledInterval?: number
|
|
24
50
|
maxStalledCount?: number
|
|
@@ -27,6 +53,7 @@ interface QueueFactoryConfigBase {
|
|
|
27
53
|
}
|
|
28
54
|
|
|
29
55
|
interface QueueFactoryConfigInline<TJob> extends QueueFactoryConfigBase {
|
|
56
|
+
prepare?: (job: Job<TJob>) => Promise<void>
|
|
30
57
|
processor: (job: Job<TJob>) => Promise<unknown>
|
|
31
58
|
processorPath?: never
|
|
32
59
|
}
|
|
@@ -38,93 +65,208 @@ interface QueueFactoryConfigFile extends QueueFactoryConfigBase {
|
|
|
38
65
|
|
|
39
66
|
export type QueueFactoryConfig<TJob> = QueueFactoryConfigInline<TJob> | QueueFactoryConfigFile
|
|
40
67
|
|
|
68
|
+
interface QueueFactoryWithDepsConfig<TJob, TDeps> extends QueueFactoryConfigBase {
|
|
69
|
+
prepare?: (deps: TDeps, job: Job<TJob>) => Promise<void>
|
|
70
|
+
processor: (deps: TDeps, job: Job<TJob>) => Promise<unknown>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface QueueWorkerConfigInline<TJob> {
|
|
74
|
+
prepare?: (job: Job<TJob>) => Promise<void>
|
|
75
|
+
processor: (job: Job<TJob>) => Promise<unknown>
|
|
76
|
+
processorPath?: never
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface QueueWorkerConfigFile {
|
|
80
|
+
processor?: never
|
|
81
|
+
processorPath: string
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type QueueWorkerConfig<TJob> = QueueWorkerConfigInline<TJob> | QueueWorkerConfigFile
|
|
85
|
+
|
|
86
|
+
export interface QueueFactoryWorkerOptions {
|
|
87
|
+
registerSignals?: boolean
|
|
88
|
+
connectionProvider?: () => IORedis
|
|
89
|
+
}
|
|
90
|
+
|
|
41
91
|
export interface QueueFactory<TJob> {
|
|
42
|
-
getQueue: () =>
|
|
92
|
+
getQueue: () => QueueShape<TJob>
|
|
43
93
|
enqueue: (job: TJob, options?: JobsOptions) => Promise<void>
|
|
44
|
-
startWorker: (options?:
|
|
94
|
+
startWorker: (options?: QueueFactoryWorkerOptions) => WorkerHandle
|
|
45
95
|
}
|
|
46
96
|
|
|
47
|
-
export
|
|
48
|
-
|
|
97
|
+
export interface QueueFactoryWithDeps<TJob, TDeps> {
|
|
98
|
+
getQueue: () => QueueShape<TJob>
|
|
99
|
+
enqueue: (job: TJob, options?: JobsOptions) => Promise<void>
|
|
100
|
+
startWorker: (options: QueueFactoryWorkerOptions & { deps: TDeps }) => WorkerHandle
|
|
101
|
+
}
|
|
49
102
|
|
|
50
|
-
|
|
51
|
-
|
|
103
|
+
export function recordEnqueuedJobMetadata(params: {
|
|
104
|
+
queueName: string
|
|
105
|
+
job: Omit<TrackedBullJobLike, 'queueName'>
|
|
106
|
+
logger?: LotaLogger
|
|
107
|
+
}): Effect.Effect<string | undefined> {
|
|
108
|
+
const logger = params.logger ?? serverLogger
|
|
109
|
+
return getQueueJobService()
|
|
110
|
+
.recordEnqueued({ queueName: params.queueName, ...params.job })
|
|
111
|
+
.pipe(
|
|
112
|
+
Effect.tapError((error) =>
|
|
113
|
+
Effect.sync(() => {
|
|
114
|
+
logger.error`Failed to persist queued job metadata (queue=${params.queueName}, job=${params.job.id}): ${error}`
|
|
115
|
+
}),
|
|
116
|
+
),
|
|
117
|
+
Effect.orElseSucceed(() => undefined),
|
|
118
|
+
)
|
|
119
|
+
}
|
|
52
120
|
|
|
53
|
-
|
|
121
|
+
function createQueueFactoryRuntime<TJob>(config: QueueFactoryConfigBase): {
|
|
122
|
+
getQueue: () => QueueShape<TJob>
|
|
123
|
+
enqueue: (job: TJob, options?: JobsOptions) => Promise<void>
|
|
124
|
+
startWorker: (workerConfig: QueueWorkerConfig<TJob>, options?: QueueFactoryWorkerOptions) => WorkerHandle
|
|
125
|
+
} {
|
|
126
|
+
type State = {
|
|
127
|
+
queue: QueueShape<TJob> | null
|
|
128
|
+
rawQueue: QueueShape<TJob> | null
|
|
129
|
+
connection: IORedis | null
|
|
130
|
+
pendingClose: Promise<void> | null
|
|
131
|
+
}
|
|
54
132
|
|
|
55
|
-
|
|
56
|
-
const connection = getConnection()
|
|
57
|
-
const shouldRecreateQueue =
|
|
58
|
-
_queue === null ||
|
|
59
|
-
_queueConnection === null ||
|
|
60
|
-
_queueConnection !== connection ||
|
|
61
|
-
_queueConnection.status === 'close' ||
|
|
62
|
-
_queueConnection.status === 'end'
|
|
63
|
-
|
|
64
|
-
if (shouldRecreateQueue) {
|
|
65
|
-
if (_queue) {
|
|
66
|
-
void _queue.close().catch((error: unknown) => {
|
|
67
|
-
serverLogger.warn`Failed to close stale ${config.displayName} queue: ${error}`
|
|
68
|
-
})
|
|
69
|
-
}
|
|
133
|
+
let state: State = { queue: null, rawQueue: null, connection: null, pendingClose: null }
|
|
70
134
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
135
|
+
const resolveConnectionProvider = (): (() => IORedis) =>
|
|
136
|
+
config.connectionProvider ?? getDefaultQueueConnectionProvider()
|
|
137
|
+
|
|
138
|
+
const getConnection = (): IORedis => resolveConnectionProvider()()
|
|
139
|
+
|
|
140
|
+
const waitForPendingClose = (): Promise<void> => {
|
|
141
|
+
const pendingClose = state.pendingClose
|
|
142
|
+
if (!pendingClose) {
|
|
143
|
+
return Promise.resolve()
|
|
79
144
|
}
|
|
80
|
-
|
|
145
|
+
|
|
146
|
+
return pendingClose.finally(() => {
|
|
147
|
+
if (state.pendingClose === pendingClose) {
|
|
148
|
+
state.pendingClose = null
|
|
149
|
+
}
|
|
150
|
+
})
|
|
81
151
|
}
|
|
82
152
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
153
|
+
const wrapQueue = (queue: QueueShape<TJob>): QueueShape<TJob> =>
|
|
154
|
+
new Proxy(queue, {
|
|
155
|
+
get(target, property, receiver) {
|
|
156
|
+
if (typeof property !== 'string') {
|
|
157
|
+
return Reflect.get(target, property, receiver) as unknown
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const value = (target as unknown as Record<string, unknown>)[property]
|
|
161
|
+
if (typeof value !== 'function' || !queueMethodsThatWaitForClose.has(property as QueueMethod)) {
|
|
162
|
+
return value
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return (...args: Array<unknown>) =>
|
|
166
|
+
waitForPendingClose().then(() =>
|
|
167
|
+
Reflect.apply(value as (...methodArgs: Array<unknown>) => unknown, target, args),
|
|
168
|
+
)
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const getQueue = (): QueueShape<TJob> => {
|
|
173
|
+
const connection = getConnection()
|
|
174
|
+
const isStale =
|
|
175
|
+
state.rawQueue === null ||
|
|
176
|
+
state.queue === null ||
|
|
177
|
+
state.connection === null ||
|
|
178
|
+
state.connection !== connection ||
|
|
179
|
+
state.connection.status === 'close' ||
|
|
180
|
+
state.connection.status === 'end'
|
|
181
|
+
|
|
182
|
+
if (!isStale && state.queue) {
|
|
183
|
+
return state.queue
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (state.rawQueue) {
|
|
187
|
+
const staleQueue = state.rawQueue
|
|
188
|
+
const previousPendingClose = state.pendingClose ?? Promise.resolve()
|
|
189
|
+
state.pendingClose = previousPendingClose
|
|
190
|
+
.catch(() => undefined)
|
|
191
|
+
.then(() =>
|
|
192
|
+
staleQueue.close().catch((error: unknown) => {
|
|
193
|
+
serverLogger.warn`Failed to close stale ${config.displayName} queue: ${error}`
|
|
194
|
+
}),
|
|
195
|
+
)
|
|
196
|
+
.then(() => undefined)
|
|
100
197
|
}
|
|
198
|
+
|
|
199
|
+
const queue = new Queue<TJob, unknown, string, TJob, unknown, string>(config.name, {
|
|
200
|
+
connection: connection as QueueOptions['connection'],
|
|
201
|
+
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, ...config.defaultJobOptions },
|
|
202
|
+
})
|
|
203
|
+
const wrappedQueue = wrapQueue(queue)
|
|
204
|
+
|
|
205
|
+
state = { queue: wrappedQueue, rawQueue: queue, connection, pendingClose: state.pendingClose }
|
|
206
|
+
return wrappedQueue
|
|
101
207
|
}
|
|
102
208
|
|
|
103
|
-
const
|
|
104
|
-
|
|
209
|
+
const enqueue = (job: TJob, options?: JobsOptions): Promise<void> =>
|
|
210
|
+
Effect.runPromise(
|
|
211
|
+
Effect.gen(function* () {
|
|
212
|
+
const queuedJob = yield* Effect.tryPromise({
|
|
213
|
+
try: () => getQueue().add(config.jobName, job, options),
|
|
214
|
+
catch: (cause) =>
|
|
215
|
+
new QueueFactoryError({ message: `Failed to enqueue job on queue "${config.name}".`, cause }),
|
|
216
|
+
})
|
|
217
|
+
yield* recordEnqueuedJobMetadata({ queueName: config.name, job: queuedJob, logger: config.logger })
|
|
218
|
+
}),
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
const startWorker = (
|
|
222
|
+
workerConfig: QueueWorkerConfig<TJob>,
|
|
223
|
+
options: QueueFactoryWorkerOptions = {},
|
|
224
|
+
): WorkerHandle => {
|
|
225
|
+
const { registerSignals = import.meta.main, connectionProvider } = options
|
|
226
|
+
const logger = config.logger ?? serverLogger
|
|
105
227
|
|
|
106
228
|
const workerOptions: WorkerOptions = {
|
|
107
|
-
connection:
|
|
229
|
+
connection: (connectionProvider ?? resolveConnectionProvider())() as QueueOptions['connection'],
|
|
108
230
|
concurrency: config.concurrency,
|
|
109
231
|
...(config.lockDuration !== undefined ? { lockDuration: config.lockDuration } : {}),
|
|
110
232
|
...(config.stalledInterval !== undefined ? { stalledInterval: config.stalledInterval } : {}),
|
|
111
233
|
...(config.maxStalledCount !== undefined ? { maxStalledCount: config.maxStalledCount } : {}),
|
|
112
234
|
}
|
|
113
235
|
|
|
114
|
-
const worker =
|
|
115
|
-
? new Worker(config.name,
|
|
236
|
+
const worker = workerConfig.processorPath
|
|
237
|
+
? new Worker(config.name, workerConfig.processorPath, workerOptions)
|
|
116
238
|
: new Worker(
|
|
117
239
|
config.name,
|
|
118
|
-
createTracedWorkerProcessor(config.name, (
|
|
240
|
+
createTracedWorkerProcessor(config.name, (job) =>
|
|
241
|
+
Effect.runPromise(
|
|
242
|
+
Effect.gen(function* () {
|
|
243
|
+
const inlineWorkerConfig = workerConfig as QueueWorkerConfigInline<TJob>
|
|
244
|
+
const typedJob = job as Job<TJob>
|
|
245
|
+
const prepare = inlineWorkerConfig.prepare
|
|
246
|
+
if (prepare) {
|
|
247
|
+
yield* Effect.tryPromise({
|
|
248
|
+
try: () => prepare(typedJob),
|
|
249
|
+
catch: (cause) =>
|
|
250
|
+
new QueueFactoryError({ message: `Worker prepare failed for queue "${config.name}".`, cause }),
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
return yield* Effect.tryPromise({
|
|
254
|
+
try: () => inlineWorkerConfig.processor(typedJob),
|
|
255
|
+
catch: (cause) =>
|
|
256
|
+
new QueueFactoryError({ message: `Worker processor failed for queue "${config.name}".`, cause }),
|
|
257
|
+
})
|
|
258
|
+
}),
|
|
259
|
+
),
|
|
260
|
+
),
|
|
119
261
|
workerOptions,
|
|
120
262
|
)
|
|
121
263
|
|
|
122
|
-
attachWorkerEvents(worker, config.displayName,
|
|
264
|
+
attachWorkerEvents(worker, config.displayName, logger)
|
|
123
265
|
|
|
124
|
-
const shutdown = createWorkerShutdown(worker, config.displayName,
|
|
266
|
+
const shutdown = createWorkerShutdown(worker, config.displayName, logger)
|
|
125
267
|
|
|
126
268
|
if (registerSignals) {
|
|
127
|
-
registerShutdownSignals({ name: config.displayName, shutdown, logger
|
|
269
|
+
registerShutdownSignals({ name: config.displayName, shutdown, logger })
|
|
128
270
|
}
|
|
129
271
|
|
|
130
272
|
return { worker, shutdown }
|
|
@@ -132,3 +274,29 @@ export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): Queu
|
|
|
132
274
|
|
|
133
275
|
return { getQueue, enqueue, startWorker }
|
|
134
276
|
}
|
|
277
|
+
|
|
278
|
+
export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): QueueFactory<TJob> {
|
|
279
|
+
const runtime = createQueueFactoryRuntime<TJob>(config)
|
|
280
|
+
return {
|
|
281
|
+
getQueue: runtime.getQueue,
|
|
282
|
+
enqueue: runtime.enqueue,
|
|
283
|
+
startWorker: (options) => runtime.startWorker(config, options),
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function createQueueFactoryWithDeps<TJob, TDeps>(
|
|
288
|
+
config: QueueFactoryWithDepsConfig<TJob, TDeps>,
|
|
289
|
+
): QueueFactoryWithDeps<TJob, TDeps> {
|
|
290
|
+
const runtime = createQueueFactoryRuntime<TJob>(config)
|
|
291
|
+
const prepare = config.prepare
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
getQueue: runtime.getQueue,
|
|
295
|
+
enqueue: runtime.enqueue,
|
|
296
|
+
startWorker: ({ deps, ...options }) =>
|
|
297
|
+
runtime.startWorker(
|
|
298
|
+
{ prepare: prepare ? (job) => prepare(deps, job) : undefined, processor: (job) => config.processor(deps, job) },
|
|
299
|
+
options,
|
|
300
|
+
),
|
|
301
|
+
}
|
|
302
|
+
}
|