@lota-sdk/core 0.1.15 → 0.1.17
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/infrastructure/schema/00_identity.surql +0 -2
- package/infrastructure/schema/01_memory.surql +1 -1
- package/infrastructure/schema/02_execution_plan.surql +62 -1
- package/infrastructure/schema/03_learned_skill.surql +1 -1
- package/infrastructure/schema/06_playbook.surql +25 -0
- package/infrastructure/schema/07_institutional_memory.surql +13 -0
- package/infrastructure/schema/08_quality_metrics.surql +17 -0
- package/package.json +12 -8
- package/src/ai/definitions.ts +81 -3
- package/src/ai/embedding-cache.ts +2 -4
- package/src/ai/index.ts +0 -2
- package/src/bifrost/bifrost.ts +2 -7
- package/src/bifrost/cache-headers.ts +8 -0
- package/src/bifrost/index.ts +1 -0
- package/src/config/agent-defaults.ts +31 -21
- package/src/config/agent-types.ts +11 -0
- package/src/config/constants.ts +2 -14
- package/src/config/debug-logger.ts +5 -1
- package/src/config/index.ts +3 -0
- package/src/config/model-constants.ts +16 -34
- package/src/config/search.ts +1 -15
- package/src/create-runtime.ts +269 -178
- package/src/db/cursor-pagination.ts +3 -6
- package/src/db/index.ts +2 -0
- package/src/db/memory-store.helpers.ts +1 -3
- package/src/db/memory-store.rows.ts +7 -7
- package/src/db/memory-store.ts +14 -18
- package/src/db/memory.ts +13 -13
- package/src/db/schema-fingerprint.ts +1 -3
- package/src/db/service.ts +153 -79
- package/src/db/startup.ts +6 -10
- package/src/db/surreal-mutation.ts +43 -0
- package/src/db/tables.ts +7 -0
- package/src/db/workstream-message-row.ts +15 -0
- package/src/embeddings/provider.ts +1 -1
- package/src/queues/context-compaction.queue.ts +15 -46
- package/src/queues/delayed-node-promotion.queue.ts +41 -0
- package/src/queues/document-processor.queue.ts +2 -4
- package/src/queues/index.ts +3 -0
- package/src/queues/memory-consolidation.queue.ts +16 -51
- package/src/queues/plan-scheduler.queue.ts +97 -0
- package/src/queues/post-chat-memory.queue.ts +20 -55
- package/src/queues/queue-factory.ts +100 -0
- package/src/queues/recent-activity-title-refinement.queue.ts +15 -50
- package/src/queues/regular-chat-memory-digest.queue.ts +16 -52
- package/src/queues/skill-extraction.queue.ts +15 -47
- package/src/queues/workstream-title-generation.queue.ts +15 -47
- package/src/redis/connection.ts +6 -0
- package/src/redis/index.ts +1 -1
- package/src/redis/redis-lease-lock.ts +1 -2
- package/src/redis/stream-context.ts +11 -0
- package/src/runtime/agent-runtime-policy.ts +109 -35
- package/src/runtime/approval-continuation.ts +12 -6
- package/src/runtime/context-compaction-runtime.ts +1 -1
- package/src/runtime/context-compaction.ts +24 -64
- package/src/runtime/execution-plan.ts +22 -18
- package/src/runtime/graph-designer.ts +15 -0
- package/src/runtime/helper-model.ts +9 -197
- package/src/runtime/index.ts +3 -1
- package/src/runtime/llm-content.ts +1 -1
- package/src/runtime/memory-block.ts +9 -11
- package/src/runtime/memory-pipeline.ts +6 -9
- package/src/runtime/plugin-resolution.ts +35 -0
- package/src/runtime/plugin-types.ts +72 -0
- package/src/runtime/retrieval-adapters.ts +1 -1
- package/src/runtime/runtime-config.ts +111 -14
- package/src/runtime/runtime-extensions.ts +2 -3
- package/src/runtime/runtime-worker-registry.ts +6 -0
- package/src/runtime/social-chat.ts +752 -0
- package/src/runtime/team-consultation-orchestrator.ts +45 -32
- package/src/runtime/team-consultation-prompts.ts +11 -2
- package/src/runtime/title-helpers.ts +2 -4
- package/src/runtime/workstream-chat-helpers.ts +1 -1
- package/src/services/adaptive-playbook.service.ts +152 -0
- package/src/services/agent-executor.service.ts +292 -0
- package/src/services/artifact-provenance.service.ts +172 -0
- package/src/services/attachment.service.ts +6 -11
- package/src/services/context-compaction.service.ts +72 -55
- package/src/services/context-enrichment.service.ts +33 -0
- package/src/services/coordination-registry.service.ts +117 -0
- package/src/services/document-chunk.service.ts +2 -4
- package/src/services/domain-agent-executor.service.ts +71 -0
- package/src/services/execution-plan.service.ts +269 -50
- package/src/services/feedback-loop.service.ts +96 -0
- package/src/services/global-orchestrator.service.ts +148 -0
- package/src/services/index.ts +27 -0
- package/src/services/institutional-memory.service.ts +145 -0
- package/src/services/learned-skill.service.ts +24 -5
- package/src/services/memory-assessment.service.ts +3 -2
- package/src/services/memory-utils.ts +3 -8
- package/src/services/memory.service.ts +49 -61
- package/src/services/monitoring-window.service.ts +86 -0
- package/src/services/mutating-approval.service.ts +1 -1
- package/src/services/node-workspace.service.ts +155 -0
- package/src/services/notification.service.ts +39 -0
- package/src/services/organization-member.service.ts +11 -4
- package/src/services/organization.service.ts +5 -5
- package/src/services/ownership-dispatcher.service.ts +403 -0
- package/src/services/plan-approval.service.ts +1 -1
- package/src/services/plan-builder.service.ts +1 -0
- package/src/services/plan-checkpoint.service.ts +30 -2
- package/src/services/plan-compiler.service.ts +5 -0
- package/src/services/plan-coordination.service.ts +152 -0
- package/src/services/plan-cycle.service.ts +284 -0
- package/src/services/plan-deadline.service.ts +287 -0
- package/src/services/plan-executor.service.ts +384 -40
- package/src/services/plan-run.service.ts +41 -7
- package/src/services/plan-scheduler.service.ts +240 -0
- package/src/services/plan-template.service.ts +117 -0
- package/src/services/plan-validator.service.ts +84 -2
- package/src/services/plan-workspace.service.ts +83 -0
- package/src/services/playbook-registry.service.ts +67 -0
- package/src/services/plugin-executor.service.ts +103 -0
- package/src/services/quality-metrics.service.ts +132 -0
- package/src/services/recent-activity.service.ts +28 -34
- package/src/services/skill-resolver.service.ts +19 -0
- package/src/services/social-chat-history.service.ts +197 -0
- package/src/services/system-executor.service.ts +105 -0
- package/src/services/workstream-message.service.ts +13 -37
- package/src/services/workstream-plan-registry.service.ts +22 -0
- package/src/services/workstream-title.service.ts +3 -1
- package/src/services/workstream-turn-preparation.service.ts +34 -89
- package/src/services/workstream.service.ts +33 -55
- package/src/services/workstream.types.ts +9 -9
- package/src/services/write-intent-validator.service.ts +81 -0
- package/src/storage/attachment-parser.ts +1 -1
- package/src/storage/attachment-utils.ts +1 -1
- package/src/storage/generated-document-storage.service.ts +3 -2
- package/src/system-agents/context-compaction.agent.ts +2 -0
- package/src/system-agents/delegated-agent-factory.ts +5 -0
- package/src/system-agents/memory-reranker.agent.ts +4 -2
- package/src/system-agents/memory.agent.ts +2 -0
- package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -0
- package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -0
- package/src/system-agents/skill-extractor.agent.ts +2 -0
- package/src/system-agents/skill-manager.agent.ts +2 -0
- package/src/system-agents/title-generator.agent.ts +2 -0
- package/src/tools/execution-plan.tool.ts +17 -23
- package/src/tools/index.ts +0 -1
- package/src/tools/research-topic.tool.ts +2 -0
- package/src/tools/team-think.tool.ts +5 -6
- package/src/utils/async.ts +2 -1
- package/src/utils/date-time.ts +4 -32
- package/src/utils/env.ts +8 -0
- package/src/utils/errors.ts +42 -10
- package/src/utils/index.ts +9 -0
- package/src/utils/string.ts +114 -1
- package/src/workers/index.ts +1 -0
- package/src/workers/regular-chat-memory-digest.helpers.ts +1 -1
- package/src/workers/regular-chat-memory-digest.runner.ts +45 -12
- package/src/workers/skill-extraction.runner.ts +26 -6
- package/src/workers/utils/file-section-chunker.ts +2 -1
- package/src/workers/utils/repo-structure-extractor.ts +2 -2
- package/src/workers/utils/repomix-file-sections.ts +2 -2
- package/src/workers/utils/sandbox-error.ts +11 -2
- package/src/workers/utils/workstream-message-query.ts +14 -25
- package/src/workers/worker-utils.ts +2 -2
- package/src/runtime/workstream-routing-policy.ts +0 -267
- package/src/tools/log-hello-world.tool.ts +0 -17
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Queue, Worker } from 'bullmq'
|
|
2
|
+
import type { Job, JobsOptions, WorkerOptions } from 'bullmq'
|
|
3
|
+
import type IORedis from 'ioredis'
|
|
4
|
+
|
|
5
|
+
import { serverLogger } from '../config/logger'
|
|
6
|
+
import { getRedisConnectionForBullMQ } from '../redis'
|
|
7
|
+
import {
|
|
8
|
+
attachWorkerEvents,
|
|
9
|
+
createTracedWorkerProcessor,
|
|
10
|
+
createWorkerShutdown,
|
|
11
|
+
DEFAULT_JOB_RETENTION,
|
|
12
|
+
registerShutdownSignals,
|
|
13
|
+
} from '../workers/worker-utils'
|
|
14
|
+
import type { WorkerHandle } from '../workers/worker-utils'
|
|
15
|
+
|
|
16
|
+
interface QueueFactoryConfigBase {
|
|
17
|
+
name: string
|
|
18
|
+
displayName: string
|
|
19
|
+
jobName: string
|
|
20
|
+
concurrency: number
|
|
21
|
+
lockDuration?: number
|
|
22
|
+
stalledInterval?: number
|
|
23
|
+
maxStalledCount?: number
|
|
24
|
+
defaultJobOptions?: JobsOptions
|
|
25
|
+
connectionProvider?: () => IORedis
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface QueueFactoryConfigInline<TJob> extends QueueFactoryConfigBase {
|
|
29
|
+
processor: (job: Job<TJob>) => Promise<void>
|
|
30
|
+
processorPath?: never
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface QueueFactoryConfigFile extends QueueFactoryConfigBase {
|
|
34
|
+
processor?: never
|
|
35
|
+
processorPath: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type QueueFactoryConfig<TJob> = QueueFactoryConfigInline<TJob> | QueueFactoryConfigFile
|
|
39
|
+
|
|
40
|
+
export interface QueueFactory<TJob> {
|
|
41
|
+
getQueue: () => Queue<TJob, unknown, string>
|
|
42
|
+
enqueue: (job: TJob, options?: JobsOptions) => Promise<void>
|
|
43
|
+
startWorker: (options?: { registerSignals?: boolean }) => WorkerHandle
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function createQueueFactory<TJob>(config: QueueFactoryConfig<TJob>): QueueFactory<TJob> {
|
|
47
|
+
let _queue: Queue<TJob, unknown, string> | null = null
|
|
48
|
+
|
|
49
|
+
const getConnection = () => config.connectionProvider?.() ?? getRedisConnectionForBullMQ()
|
|
50
|
+
|
|
51
|
+
const getQueue = (): Queue<TJob, unknown, string> => {
|
|
52
|
+
if (!_queue) {
|
|
53
|
+
_queue = new Queue<TJob, unknown, string>(config.name, {
|
|
54
|
+
connection: getConnection(),
|
|
55
|
+
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, ...config.defaultJobOptions },
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
return _queue
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type QueueAdd = Queue<TJob, unknown, string>['add']
|
|
62
|
+
const jobName = config.jobName as Parameters<QueueAdd>[0]
|
|
63
|
+
const toData = (job: TJob) => job as Parameters<QueueAdd>[1]
|
|
64
|
+
|
|
65
|
+
const enqueue = async (job: TJob, options?: JobsOptions): Promise<void> => {
|
|
66
|
+
await getQueue().add(jobName, toData(job), options)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const startWorker = (options: { registerSignals?: boolean } = {}): WorkerHandle => {
|
|
70
|
+
const { registerSignals = import.meta.main } = options
|
|
71
|
+
|
|
72
|
+
const workerOptions: WorkerOptions = {
|
|
73
|
+
connection: getConnection(),
|
|
74
|
+
concurrency: config.concurrency,
|
|
75
|
+
...(config.lockDuration !== undefined ? { lockDuration: config.lockDuration } : {}),
|
|
76
|
+
...(config.stalledInterval !== undefined ? { stalledInterval: config.stalledInterval } : {}),
|
|
77
|
+
...(config.maxStalledCount !== undefined ? { maxStalledCount: config.maxStalledCount } : {}),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const worker = config.processorPath
|
|
81
|
+
? new Worker(config.name, config.processorPath, workerOptions)
|
|
82
|
+
: new Worker(
|
|
83
|
+
config.name,
|
|
84
|
+
createTracedWorkerProcessor(config.name, (config as QueueFactoryConfigInline<TJob>).processor),
|
|
85
|
+
workerOptions,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
attachWorkerEvents(worker, config.displayName, serverLogger)
|
|
89
|
+
|
|
90
|
+
const shutdown = createWorkerShutdown(worker, config.displayName, serverLogger)
|
|
91
|
+
|
|
92
|
+
if (registerSignals) {
|
|
93
|
+
registerShutdownSignals({ name: config.displayName, shutdown, logger: serverLogger })
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { worker, shutdown }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { getQueue, enqueue, startWorker }
|
|
100
|
+
}
|
|
@@ -1,65 +1,30 @@
|
|
|
1
|
-
import { Queue, Worker } from 'bullmq'
|
|
2
1
|
import type { Job } from 'bullmq'
|
|
3
2
|
|
|
4
|
-
import { serverLogger } from '../config/logger'
|
|
5
3
|
import { databaseService } from '../db/service'
|
|
6
|
-
import { getRedisConnectionForBullMQ } from '../redis'
|
|
7
4
|
import { recentActivityTitleService } from '../services/recent-activity-title.service'
|
|
8
|
-
import {
|
|
9
|
-
attachWorkerEvents,
|
|
10
|
-
createTracedWorkerProcessor,
|
|
11
|
-
createWorkerShutdown,
|
|
12
|
-
DEFAULT_JOB_RETENTION,
|
|
13
|
-
registerShutdownSignals,
|
|
14
|
-
} from '../workers/worker-utils'
|
|
15
|
-
import type { WorkerHandle } from '../workers/worker-utils'
|
|
5
|
+
import { createQueueFactory } from './queue-factory'
|
|
16
6
|
|
|
17
7
|
interface RecentActivityTitleRefinementJob {
|
|
18
8
|
activityId: string
|
|
19
9
|
}
|
|
20
10
|
|
|
21
|
-
const RECENT_ACTIVITY_TITLE_REFINEMENT_QUEUE = 'recent-activity-title-refinement'
|
|
22
|
-
|
|
23
|
-
let _recentActivityTitleRefinementQueue: Queue<RecentActivityTitleRefinementJob> | null = null
|
|
24
|
-
function getRecentActivityTitleRefinementQueue(): Queue<RecentActivityTitleRefinementJob> {
|
|
25
|
-
if (!_recentActivityTitleRefinementQueue) {
|
|
26
|
-
_recentActivityTitleRefinementQueue = new Queue<RecentActivityTitleRefinementJob>(
|
|
27
|
-
RECENT_ACTIVITY_TITLE_REFINEMENT_QUEUE,
|
|
28
|
-
{
|
|
29
|
-
connection: getRedisConnectionForBullMQ(),
|
|
30
|
-
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, attempts: 3, backoff: { type: 'exponential', delay: 2_000 } },
|
|
31
|
-
},
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
return _recentActivityTitleRefinementQueue
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export async function enqueueRecentActivityTitleRefinement(job: RecentActivityTitleRefinementJob) {
|
|
38
|
-
return await getRecentActivityTitleRefinementQueue().add('refine-recent-activity-title', job, {
|
|
39
|
-
jobId: `recent-activity-title:${job.activityId}`,
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
11
|
async function processRecentActivityTitleRefinementJob(job: Job<RecentActivityTitleRefinementJob>): Promise<void> {
|
|
44
12
|
await databaseService.connect()
|
|
45
13
|
await recentActivityTitleService.refineRecentActivityTitle(job.data.activityId)
|
|
46
14
|
}
|
|
47
15
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (registerSignals) {
|
|
61
|
-
registerShutdownSignals({ name: 'Recent activity title refinement', shutdown, logger: serverLogger })
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return { worker, shutdown }
|
|
16
|
+
const recentActivityTitleRefinement = createQueueFactory<RecentActivityTitleRefinementJob>({
|
|
17
|
+
name: 'recent-activity-title-refinement',
|
|
18
|
+
displayName: 'Recent activity title refinement',
|
|
19
|
+
jobName: 'refine-recent-activity-title',
|
|
20
|
+
concurrency: 10,
|
|
21
|
+
lockDuration: 300_000,
|
|
22
|
+
defaultJobOptions: { attempts: 3, backoff: { type: 'exponential', delay: 2_000 } },
|
|
23
|
+
processor: processRecentActivityTitleRefinementJob,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export function enqueueRecentActivityTitleRefinement(job: RecentActivityTitleRefinementJob) {
|
|
27
|
+
return recentActivityTitleRefinement.enqueue(job, { jobId: `recent-activity-title:${job.activityId}` })
|
|
65
28
|
}
|
|
29
|
+
|
|
30
|
+
export const startRecentActivityTitleRefinementWorker = recentActivityTitleRefinement.startWorker
|
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { serverLogger } from '../config/logger'
|
|
4
|
-
import { getRedisConnectionForBullMQ } from '../redis'
|
|
5
|
-
import {
|
|
6
|
-
attachWorkerEvents,
|
|
7
|
-
DEFAULT_JOB_RETENTION,
|
|
8
|
-
getWorkerPath,
|
|
9
|
-
LONG_JOB_LOCK_DURATION_MS,
|
|
10
|
-
createWorkerShutdown,
|
|
11
|
-
registerShutdownSignals,
|
|
12
|
-
} from '../workers/worker-utils'
|
|
13
|
-
import type { WorkerHandle } from '../workers/worker-utils'
|
|
1
|
+
import { getWorkerPath, LONG_JOB_LOCK_DURATION_MS } from '../workers/worker-utils'
|
|
2
|
+
import { createQueueFactory } from './queue-factory'
|
|
14
3
|
import {
|
|
15
4
|
buildRegularChatMemoryDigestDeduplicationId,
|
|
16
5
|
buildRegularChatMemoryDigestJobOptions,
|
|
@@ -20,50 +9,25 @@ export interface RegularChatMemoryDigestJob {
|
|
|
20
9
|
orgId: string
|
|
21
10
|
}
|
|
22
11
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export async function enqueueRegularChatMemoryDigest(job: RegularChatMemoryDigestJob) {
|
|
37
|
-
return await getRegularChatMemoryDigestQueue().add(
|
|
38
|
-
'run-digest',
|
|
39
|
-
job,
|
|
40
|
-
buildRegularChatMemoryDigestJobOptions(job.orgId),
|
|
41
|
-
)
|
|
12
|
+
const regularChatMemoryDigest = createQueueFactory<RegularChatMemoryDigestJob>({
|
|
13
|
+
name: 'regular-chat-memory-digest',
|
|
14
|
+
displayName: 'Regular chat memory digest',
|
|
15
|
+
jobName: 'run-digest',
|
|
16
|
+
concurrency: 1,
|
|
17
|
+
lockDuration: LONG_JOB_LOCK_DURATION_MS,
|
|
18
|
+
defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
|
|
19
|
+
processorPath: getWorkerPath('regular-chat-memory-digest.worker.ts'),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export function enqueueRegularChatMemoryDigest(job: RegularChatMemoryDigestJob) {
|
|
23
|
+
return regularChatMemoryDigest.enqueue(job, buildRegularChatMemoryDigestJobOptions(job.orgId))
|
|
42
24
|
}
|
|
43
25
|
|
|
44
26
|
export async function clearRegularChatMemoryDigestDeduplicationKey(orgId: string): Promise<void> {
|
|
45
|
-
await
|
|
27
|
+
await regularChatMemoryDigest.getQueue().removeDeduplicationKey(buildRegularChatMemoryDigestDeduplicationId(orgId))
|
|
46
28
|
}
|
|
47
29
|
|
|
48
|
-
export
|
|
49
|
-
const { registerSignals = import.meta.main } = options
|
|
50
|
-
const processorPath = getWorkerPath('regular-chat-memory-digest.worker.ts')
|
|
51
|
-
const worker = new Worker(REGULAR_CHAT_MEMORY_DIGEST_QUEUE, processorPath, {
|
|
52
|
-
connection: getRedisConnectionForBullMQ(),
|
|
53
|
-
concurrency: 1,
|
|
54
|
-
lockDuration: LONG_JOB_LOCK_DURATION_MS,
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
attachWorkerEvents(worker, 'Regular chat memory digest', serverLogger)
|
|
58
|
-
|
|
59
|
-
const shutdown = createWorkerShutdown(worker, 'Regular chat memory digest', serverLogger)
|
|
60
|
-
|
|
61
|
-
if (registerSignals) {
|
|
62
|
-
registerShutdownSignals({ name: 'Regular chat memory digest', shutdown, logger: serverLogger })
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return { worker, shutdown }
|
|
66
|
-
}
|
|
30
|
+
export const startRegularChatMemoryDigestWorker = regularChatMemoryDigest.startWorker
|
|
67
31
|
|
|
68
32
|
if (import.meta.main) {
|
|
69
33
|
startRegularChatMemoryDigestWorker()
|
|
@@ -1,58 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { serverLogger } from '../config/logger'
|
|
4
|
-
import { getRedisConnectionForBullMQ } from '../redis'
|
|
5
|
-
import {
|
|
6
|
-
attachWorkerEvents,
|
|
7
|
-
DEFAULT_JOB_RETENTION,
|
|
8
|
-
getWorkerPath,
|
|
9
|
-
LONG_JOB_LOCK_DURATION_MS,
|
|
10
|
-
createWorkerShutdown,
|
|
11
|
-
registerShutdownSignals,
|
|
12
|
-
} from '../workers/worker-utils'
|
|
13
|
-
import type { WorkerHandle } from '../workers/worker-utils'
|
|
1
|
+
import { getWorkerPath } from '../workers/worker-utils'
|
|
2
|
+
import { createQueueFactory } from './queue-factory'
|
|
14
3
|
import { buildSkillExtractionJobOptions } from './skill-extraction.config'
|
|
15
4
|
|
|
16
5
|
export interface SkillExtractionJob {
|
|
17
6
|
orgId: string
|
|
18
7
|
}
|
|
19
8
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
9
|
+
const skillExtraction = createQueueFactory<SkillExtractionJob>({
|
|
10
|
+
name: 'skill-extraction',
|
|
11
|
+
displayName: 'Skill extraction',
|
|
12
|
+
jobName: 'run-extraction',
|
|
13
|
+
concurrency: 10,
|
|
14
|
+
lockDuration: 600_000,
|
|
15
|
+
defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 5000 } },
|
|
16
|
+
processorPath: getWorkerPath('skill-extraction.worker.ts'),
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export function enqueueSkillExtraction(job: SkillExtractionJob) {
|
|
20
|
+
return skillExtraction.enqueue(job, buildSkillExtractionJobOptions(job.orgId))
|
|
31
21
|
}
|
|
32
22
|
|
|
33
|
-
export
|
|
34
|
-
return await getSkillExtractionQueue().add('run-extraction', job, buildSkillExtractionJobOptions(job.orgId))
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function startSkillExtractionWorker(options: { registerSignals?: boolean } = {}): WorkerHandle {
|
|
38
|
-
const { registerSignals = import.meta.main } = options
|
|
39
|
-
const processorPath = getWorkerPath('skill-extraction.worker.ts')
|
|
40
|
-
const worker = new Worker(SKILL_EXTRACTION_QUEUE, processorPath, {
|
|
41
|
-
connection: getRedisConnectionForBullMQ(),
|
|
42
|
-
concurrency: 1,
|
|
43
|
-
lockDuration: LONG_JOB_LOCK_DURATION_MS,
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
attachWorkerEvents(worker, 'Skill extraction', serverLogger)
|
|
47
|
-
|
|
48
|
-
const shutdown = createWorkerShutdown(worker, 'Skill extraction', serverLogger)
|
|
49
|
-
|
|
50
|
-
if (registerSignals) {
|
|
51
|
-
registerShutdownSignals({ name: 'Skill extraction', shutdown, logger: serverLogger })
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return { worker, shutdown }
|
|
55
|
-
}
|
|
23
|
+
export const startSkillExtractionWorker = skillExtraction.startWorker
|
|
56
24
|
|
|
57
25
|
if (import.meta.main) {
|
|
58
26
|
startSkillExtractionWorker()
|
|
@@ -1,65 +1,33 @@
|
|
|
1
|
-
import { Queue, Worker } from 'bullmq'
|
|
2
1
|
import type { Job } from 'bullmq'
|
|
3
2
|
|
|
4
|
-
import { serverLogger } from '../config/logger'
|
|
5
3
|
import { ensureRecordId } from '../db/record-id'
|
|
6
4
|
import { databaseService } from '../db/service'
|
|
7
|
-
import { getRedisConnectionForBullMQ } from '../redis'
|
|
8
5
|
import { workstreamTitleService } from '../services/workstream-title.service'
|
|
9
|
-
import {
|
|
10
|
-
attachWorkerEvents,
|
|
11
|
-
createTracedWorkerProcessor,
|
|
12
|
-
createWorkerShutdown,
|
|
13
|
-
DEFAULT_JOB_RETENTION,
|
|
14
|
-
registerShutdownSignals,
|
|
15
|
-
} from '../workers/worker-utils'
|
|
16
|
-
import type { WorkerHandle } from '../workers/worker-utils'
|
|
6
|
+
import { createQueueFactory } from './queue-factory'
|
|
17
7
|
|
|
18
8
|
interface WorkstreamTitleGenerationJob {
|
|
19
9
|
workstreamId: string
|
|
20
10
|
sourceText: string
|
|
21
11
|
}
|
|
22
12
|
|
|
23
|
-
const WORKSTREAM_TITLE_GENERATION_QUEUE = 'workstream-title-generation'
|
|
24
|
-
|
|
25
|
-
let _workstreamTitleGenerationQueue: Queue<WorkstreamTitleGenerationJob> | null = null
|
|
26
|
-
function getWorkstreamTitleGenerationQueue(): Queue<WorkstreamTitleGenerationJob> {
|
|
27
|
-
if (!_workstreamTitleGenerationQueue) {
|
|
28
|
-
_workstreamTitleGenerationQueue = new Queue<WorkstreamTitleGenerationJob>(WORKSTREAM_TITLE_GENERATION_QUEUE, {
|
|
29
|
-
connection: getRedisConnectionForBullMQ(),
|
|
30
|
-
defaultJobOptions: { ...DEFAULT_JOB_RETENTION, attempts: 2, backoff: { type: 'exponential', delay: 2_000 } },
|
|
31
|
-
})
|
|
32
|
-
}
|
|
33
|
-
return _workstreamTitleGenerationQueue
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export async function enqueueWorkstreamTitleGeneration(job: WorkstreamTitleGenerationJob) {
|
|
37
|
-
return await getWorkstreamTitleGenerationQueue().add('generate-workstream-title', job, {
|
|
38
|
-
jobId: `workstream-title:${job.workstreamId}`,
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
|
|
42
13
|
async function processWorkstreamTitleGenerationJob(job: Job<WorkstreamTitleGenerationJob>): Promise<void> {
|
|
43
14
|
await databaseService.connect()
|
|
44
15
|
const workstreamRef = ensureRecordId(job.data.workstreamId)
|
|
45
16
|
await workstreamTitleService.generateAndPersistTitle(workstreamRef, job.data.sourceText)
|
|
46
17
|
}
|
|
47
18
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (registerSignals) {
|
|
61
|
-
registerShutdownSignals({ name: 'Workstream title generation', shutdown, logger: serverLogger })
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return { worker, shutdown }
|
|
19
|
+
const workstreamTitleGeneration = createQueueFactory<WorkstreamTitleGenerationJob>({
|
|
20
|
+
name: 'workstream-title-generation',
|
|
21
|
+
displayName: 'Workstream title generation',
|
|
22
|
+
jobName: 'generate-workstream-title',
|
|
23
|
+
concurrency: 10,
|
|
24
|
+
lockDuration: 60_000,
|
|
25
|
+
defaultJobOptions: { attempts: 2, backoff: { type: 'exponential', delay: 2_000 } },
|
|
26
|
+
processor: processWorkstreamTitleGenerationJob,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export function enqueueWorkstreamTitleGeneration(job: WorkstreamTitleGenerationJob) {
|
|
30
|
+
return workstreamTitleGeneration.enqueue(job, { jobId: `workstream-title:${job.workstreamId}` })
|
|
65
31
|
}
|
|
32
|
+
|
|
33
|
+
export const startWorkstreamTitleGenerationWorker = workstreamTitleGeneration.startWorker
|
package/src/redis/connection.ts
CHANGED
|
@@ -58,6 +58,7 @@ class RedisConnectionManagerImpl implements RedisConnectionManager {
|
|
|
58
58
|
private healthCheckInterval: ReturnType<typeof setInterval> | null = null
|
|
59
59
|
private isInitialized = false
|
|
60
60
|
private isClosing = false
|
|
61
|
+
private isHealthCheckRunning = false
|
|
61
62
|
|
|
62
63
|
constructor(private readonly options: CreateRedisConnectionManagerOptions) {
|
|
63
64
|
this.initializeConnection()
|
|
@@ -128,6 +129,8 @@ class RedisConnectionManagerImpl implements RedisConnectionManager {
|
|
|
128
129
|
|
|
129
130
|
const intervalMs = this.options.healthCheckIntervalMs ?? DEFAULT_HEALTH_CHECK_INTERVAL_MS
|
|
130
131
|
this.healthCheckInterval = setInterval(async () => {
|
|
132
|
+
if (this.isHealthCheckRunning) return
|
|
133
|
+
this.isHealthCheckRunning = true
|
|
131
134
|
try {
|
|
132
135
|
if (this.redis && this.redis.status === 'ready') {
|
|
133
136
|
await this.redis.ping()
|
|
@@ -136,8 +139,11 @@ class RedisConnectionManagerImpl implements RedisConnectionManager {
|
|
|
136
139
|
} catch (error) {
|
|
137
140
|
log(this.options.logger, 'warn', `Redis health check failed: ${getErrorMessage(error)}`)
|
|
138
141
|
this.isHealthy = false
|
|
142
|
+
} finally {
|
|
143
|
+
this.isHealthCheckRunning = false
|
|
139
144
|
}
|
|
140
145
|
}, intervalMs)
|
|
146
|
+
this.healthCheckInterval.unref()
|
|
141
147
|
}
|
|
142
148
|
|
|
143
149
|
getConnection(): IORedis {
|
package/src/redis/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export {
|
|
|
9
9
|
} from './connection-accessor'
|
|
10
10
|
export { withOrgMemoryLock } from './org-memory-lock'
|
|
11
11
|
export { LeaseLockLostError, withRedisLeaseLock } from './redis-lease-lock'
|
|
12
|
-
export { createWorkstreamResumableContext } from './stream-context'
|
|
12
|
+
export { closeSharedSubscriber, createWorkstreamResumableContext } from './stream-context'
|
|
13
13
|
|
|
14
14
|
export { createRedisConnectionManager }
|
|
15
15
|
export type { RedisConnectionManager }
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto'
|
|
2
1
|
import { setTimeout as delay } from 'node:timers/promises'
|
|
3
2
|
|
|
4
3
|
import type IORedis from 'ioredis'
|
|
@@ -147,7 +146,7 @@ export async function withRedisLeaseLock<T>(
|
|
|
147
146
|
const heldInfoThresholdMs = options.heldInfoThresholdMs ?? 5_000
|
|
148
147
|
const label = options.label ?? 'redis lease lock'
|
|
149
148
|
|
|
150
|
-
const lockValue = randomUUID()
|
|
149
|
+
const lockValue = crypto.randomUUID()
|
|
151
150
|
const waitStart = Date.now()
|
|
152
151
|
await acquireLeaseLock({ ...options, lockKey, lockValue, label, retryDelayMs, waitLogIntervalMs, maxWaitMs })
|
|
153
152
|
const waitedMs = Date.now() - waitStart
|
|
@@ -44,6 +44,17 @@ function getSharedSubscriber(): Subscriber {
|
|
|
44
44
|
return sharedSubscriber.subscriber
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
export async function closeSharedSubscriber(): Promise<void> {
|
|
48
|
+
if (!sharedSubscriber) return
|
|
49
|
+
const { client } = sharedSubscriber
|
|
50
|
+
sharedSubscriber = undefined
|
|
51
|
+
try {
|
|
52
|
+
await client.quit()
|
|
53
|
+
} catch {
|
|
54
|
+
client.disconnect()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
47
58
|
export function createWorkstreamResumableContext() {
|
|
48
59
|
const redis = getRedisConnection()
|
|
49
60
|
return createResumableStreamContext({
|