@lota-sdk/core 0.4.27 → 0.4.28
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lota-sdk/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.28",
|
|
4
4
|
"files": [
|
|
5
5
|
"src",
|
|
6
6
|
"infrastructure/schema"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@ai-sdk/provider": "^3.0.9",
|
|
33
33
|
"@chat-adapter/slack": "^4.26.0",
|
|
34
34
|
"@chat-adapter/state-ioredis": "^4.26.0",
|
|
35
|
-
"@lota-sdk/shared": "0.4.
|
|
35
|
+
"@lota-sdk/shared": "0.4.28",
|
|
36
36
|
"@mendable/firecrawl-js": "^4.20.0",
|
|
37
37
|
"@surrealdb/node": "^3.0.3",
|
|
38
38
|
"ai": "^6.0.170",
|
|
@@ -9,6 +9,7 @@ import { ERROR_TAGS } from '../effect/errors'
|
|
|
9
9
|
import type { TrackedBullJobLike } from '../services/queue-job.service'
|
|
10
10
|
import {
|
|
11
11
|
attachWorkerEvents,
|
|
12
|
+
attachSandboxChildRecycling,
|
|
12
13
|
createTracedWorkerProcessor,
|
|
13
14
|
createWorkerShutdown,
|
|
14
15
|
DEFAULT_JOB_RETENTION,
|
|
@@ -42,6 +43,7 @@ interface QueueFactoryConfigBase {
|
|
|
42
43
|
stalledInterval?: number
|
|
43
44
|
maxStalledCount?: number
|
|
44
45
|
defaultJobOptions?: JobsOptions
|
|
46
|
+
recycleSandboxChildren?: boolean
|
|
45
47
|
connectionProvider: () => IORedis
|
|
46
48
|
queueJobService: QueueJobService
|
|
47
49
|
}
|
|
@@ -280,6 +282,9 @@ function createQueueFactoryRuntime<TJob>(config: QueueFactoryConfigBase): {
|
|
|
280
282
|
// leaks.
|
|
281
283
|
try {
|
|
282
284
|
attachWorkerEvents(worker, config.displayName, logger)
|
|
285
|
+
if ((config.recycleSandboxChildren ?? true) && workerConfig.processorPath) {
|
|
286
|
+
attachSandboxChildRecycling(worker, config.displayName, logger)
|
|
287
|
+
}
|
|
283
288
|
const shutdown = createWorkerShutdown(worker, config.displayName, logger)
|
|
284
289
|
|
|
285
290
|
if (registerSignals) {
|
|
@@ -4,7 +4,7 @@ import { AiGatewayModelsTag, isAiGenerationContentFilterError } from '../ai-gate
|
|
|
4
4
|
import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
|
|
5
5
|
import type { ResolvedAgentConfig } from '../config/agent-defaults'
|
|
6
6
|
import { chatLogger } from '../config/logger'
|
|
7
|
-
import {
|
|
7
|
+
import { ServiceError } from '../effect/errors'
|
|
8
8
|
import { AgentConfigServiceTag } from '../effect/services'
|
|
9
9
|
import type { HelperModelRuntime } from '../runtime/helper-model'
|
|
10
10
|
import { HelperModelTag } from '../runtime/helper-model'
|
|
@@ -13,10 +13,19 @@ import {
|
|
|
13
13
|
makeRecentActivityTitleRefinerAgentFactory,
|
|
14
14
|
RECENT_ACTIVITY_TITLE_REFINER_PROMPT,
|
|
15
15
|
} from '../system-agents/recent-activity-title-refiner.agent'
|
|
16
|
+
import { compactWhitespace, truncateText } from '../utils/string'
|
|
16
17
|
import type { makeRecentActivityService } from './recent-activity.service'
|
|
17
18
|
import { RecentActivityServiceTag } from './recent-activity.service'
|
|
18
19
|
|
|
19
20
|
const RECENT_ACTIVITY_TITLE_TIMEOUT_MS = 60_000
|
|
21
|
+
const RECENT_ACTIVITY_TITLE_FIELD_MAX_CHARS = 800
|
|
22
|
+
|
|
23
|
+
function formatPromptField(label: string, value: string | undefined): string | null {
|
|
24
|
+
if (!value) return null
|
|
25
|
+
const normalized = compactWhitespace(value)
|
|
26
|
+
if (!normalized) return null
|
|
27
|
+
return `${label}=${truncateText(normalized, RECENT_ACTIVITY_TITLE_FIELD_MAX_CHARS)}`
|
|
28
|
+
}
|
|
20
29
|
|
|
21
30
|
function buildRefinementPromptInput(
|
|
22
31
|
candidate: {
|
|
@@ -32,12 +41,12 @@ function buildRefinementPromptInput(
|
|
|
32
41
|
|
|
33
42
|
const metadata = candidate.metadata
|
|
34
43
|
const lines = [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
formatPromptField('sourceLabel', candidate.sourceLabel),
|
|
45
|
+
formatPromptField('systemTitle', candidate.systemTitle),
|
|
46
|
+
formatPromptField('agentName', metadata.agentName),
|
|
47
|
+
formatPromptField('threadTitle', metadata.threadTitle),
|
|
48
|
+
formatPromptField('userMessage', metadata.userMessageText),
|
|
49
|
+
formatPromptField('assistantSummary', metadata.assistantSummary),
|
|
41
50
|
].filter((line): line is string => Boolean(line))
|
|
42
51
|
|
|
43
52
|
if (lines.length === 0) return null
|
|
@@ -77,13 +86,12 @@ export function makeRecentActivityTitleService(
|
|
|
77
86
|
? cause
|
|
78
87
|
: new ServiceError({ message: 'Failed to generate recent activity title refinement.', cause }),
|
|
79
88
|
}).pipe(
|
|
80
|
-
Effect.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
: Effect.fail(error),
|
|
89
|
+
Effect.catch((error) =>
|
|
90
|
+
Effect.sync(() => {
|
|
91
|
+
const reason = isAiGenerationContentFilterError(error) ? 'provider content filter' : 'non-fatal error'
|
|
92
|
+
chatLogger.warn`Skipping recent activity title refinement after ${reason} (activityId=${activityId}): ${error}`
|
|
93
|
+
return null
|
|
94
|
+
}),
|
|
87
95
|
),
|
|
88
96
|
)
|
|
89
97
|
if (maybeRefinedTitle === null) {
|
|
@@ -95,6 +95,60 @@ export const createWorkerShutdown = (worker: Worker, name: string, logger: typeo
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
interface SandboxChildLike {
|
|
99
|
+
pid?: number
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface SandboxChildPoolLike {
|
|
103
|
+
getAllFree?: () => SandboxChildLike[]
|
|
104
|
+
kill?: (child: SandboxChildLike, signal?: NodeJS.Signals) => Promise<void>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface SandboxedWorkerLike {
|
|
108
|
+
childPool?: SandboxChildPoolLike
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function getSandboxChildPool(worker: Worker): SandboxChildPoolLike | null {
|
|
112
|
+
const pool = (worker as unknown as SandboxedWorkerLike).childPool
|
|
113
|
+
return pool && typeof pool.getAllFree === 'function' && typeof pool.kill === 'function' ? pool : null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function recycleIdleSandboxChildren(
|
|
117
|
+
worker: Worker,
|
|
118
|
+
name: string,
|
|
119
|
+
logger: typeof chatLogger = chatLogger,
|
|
120
|
+
): Promise<void> {
|
|
121
|
+
const pool = getSandboxChildPool(worker)
|
|
122
|
+
if (!pool) return Promise.resolve()
|
|
123
|
+
|
|
124
|
+
const idleChildren = pool.getAllFree?.() ?? []
|
|
125
|
+
if (idleChildren.length === 0) return Promise.resolve()
|
|
126
|
+
|
|
127
|
+
return Promise.all(
|
|
128
|
+
idleChildren.map((child) =>
|
|
129
|
+
(pool.kill?.(child, 'SIGTERM') ?? Promise.resolve()).catch((error: unknown) => {
|
|
130
|
+
logger.warn`Failed to recycle idle ${name} sandbox child (${child.pid ?? 'unknown'}): ${error}`
|
|
131
|
+
}),
|
|
132
|
+
),
|
|
133
|
+
).then(() => undefined)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function attachSandboxChildRecycling(
|
|
137
|
+
worker: Worker,
|
|
138
|
+
name: string,
|
|
139
|
+
logger: typeof chatLogger = chatLogger,
|
|
140
|
+
): void {
|
|
141
|
+
const recycle = () => {
|
|
142
|
+
// @effect-diagnostics-next-line globalTimers:off -- BullMQ worker event callback; defer until BullMQ releases the child.
|
|
143
|
+
setTimeout(() => {
|
|
144
|
+
void recycleIdleSandboxChildren(worker, name, logger)
|
|
145
|
+
}, 0)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
worker.on('completed', recycle)
|
|
149
|
+
worker.on('failed', recycle)
|
|
150
|
+
}
|
|
151
|
+
|
|
98
152
|
export function createTracedWorkerProcessor<TJob extends TracedWorkerJobLike, TResult = void>(
|
|
99
153
|
queueName: string,
|
|
100
154
|
processor: (job: TJob) => Promise<TResult>,
|