@lota-sdk/core 0.4.13 → 0.4.14
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 +4 -4
- package/src/ai/embedding-cache.ts +17 -11
- package/src/ai-gateway/ai-gateway.ts +164 -94
- package/src/ai-gateway/index.ts +4 -1
- package/src/config/agent-defaults.ts +2 -2
- package/src/config/agent-types.ts +1 -1
- package/src/create-runtime.ts +259 -200
- package/src/db/cursor-pagination.ts +2 -9
- package/src/db/memory-store.ts +194 -175
- package/src/db/memory.ts +125 -71
- package/src/db/schema-fingerprint.ts +5 -4
- package/src/db/service-normalization.ts +4 -3
- package/src/db/service.ts +3 -2
- package/src/db/startup.ts +15 -16
- package/src/effect/errors.ts +161 -21
- package/src/effect/index.ts +0 -1
- package/src/embeddings/provider.ts +15 -7
- package/src/queues/autonomous-job.queue.ts +10 -22
- package/src/queues/delayed-node-promotion.queue.ts +8 -14
- package/src/queues/document-processor.queue.ts +13 -4
- package/src/queues/memory-consolidation.queue.ts +26 -14
- package/src/queues/plan-agent-heartbeat.queue.ts +10 -9
- package/src/queues/plan-scheduler.queue.ts +37 -15
- package/src/queues/queue-factory.ts +59 -35
- package/src/queues/standalone-worker.ts +3 -2
- package/src/redis/connection.ts +10 -3
- package/src/redis/org-memory-lock.ts +1 -1
- package/src/redis/redis-lease-lock.ts +5 -5
- package/src/redis/stream-context.ts +1 -1
- package/src/runtime/chat-message.ts +64 -1
- package/src/runtime/chat-run-orchestration.ts +33 -20
- package/src/runtime/context-compaction/context-compaction-runtime.ts +14 -7
- package/src/runtime/context-compaction/context-compaction.ts +78 -66
- package/src/runtime/domain-layer.ts +13 -7
- package/src/runtime/execution-plan.ts +7 -3
- package/src/runtime/memory/memory-block.ts +3 -9
- package/src/runtime/memory/memory-scope.ts +3 -1
- package/src/runtime/plugin-resolution.ts +2 -1
- package/src/runtime/post-turn-side-effects.ts +6 -5
- package/src/runtime/retrieval-adapters.ts +8 -20
- package/src/runtime/runtime-config.ts +3 -9
- package/src/runtime/runtime-extensions.ts +2 -4
- package/src/runtime/runtime-lifecycle.ts +56 -16
- package/src/runtime/runtime-services.ts +180 -102
- package/src/runtime/runtime-worker-registry.ts +3 -1
- package/src/runtime/social-chat/social-chat-agent-runner.ts +1 -1
- package/src/runtime/social-chat/social-chat-history.ts +21 -18
- package/src/runtime/social-chat/social-chat.ts +356 -223
- package/src/runtime/specialist-runner.ts +3 -1
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +3 -2
- package/src/runtime/thread-turn-context.ts +142 -102
- package/src/runtime/turn-lifecycle.ts +15 -46
- package/src/services/agent-activity.service.ts +1 -1
- package/src/services/agent-executor.service.ts +107 -77
- package/src/services/autonomous-job.service.ts +354 -293
- package/src/services/background-work.service.ts +3 -3
- package/src/services/context-compaction.service.ts +7 -2
- package/src/services/document-chunk.service.ts +50 -32
- package/src/services/execution-plan/execution-plan-schedule.ts +5 -3
- package/src/services/execution-plan/execution-plan.service.ts +162 -179
- package/src/services/feedback-loop.service.ts +5 -4
- package/src/services/graph-full-routing.ts +37 -36
- package/src/services/institutional-memory.service.ts +28 -30
- package/src/services/learned-skill.service.ts +107 -72
- package/src/services/memory/memory-errors.ts +4 -23
- package/src/services/memory/memory-org-memory.ts +10 -5
- package/src/services/memory/memory-rerank.ts +18 -6
- package/src/services/memory/memory.service.ts +170 -111
- package/src/services/memory/rerank.service.ts +29 -20
- package/src/services/organization-member.service.ts +1 -1
- package/src/services/organization.service.ts +69 -75
- package/src/services/ownership-dispatcher.service.ts +40 -39
- package/src/services/plan/plan-agent-heartbeat.service.ts +26 -23
- package/src/services/plan/plan-agent-query.service.ts +39 -31
- package/src/services/plan/plan-completion-side-effects.ts +13 -17
- package/src/services/plan/plan-coordination.service.ts +2 -1
- package/src/services/plan/plan-cycle.service.ts +6 -5
- package/src/services/plan/plan-deadline.service.ts +57 -54
- package/src/services/plan/plan-event-delivery.service.ts +5 -4
- package/src/services/plan/plan-executor-graph.ts +18 -15
- package/src/services/plan/plan-executor.service.ts +235 -262
- package/src/services/plan/plan-run.service.ts +169 -93
- package/src/services/plan/plan-scheduler.service.ts +192 -202
- package/src/services/plan/plan-template.service.ts +1 -1
- package/src/services/plan/plan-transaction-events.ts +1 -1
- package/src/services/plan/plan-workspace.service.ts +23 -14
- package/src/services/plugin-executor.service.ts +5 -9
- package/src/services/queue-job.service.ts +117 -59
- package/src/services/recent-activity-title.service.ts +13 -12
- package/src/services/recent-activity.service.ts +6 -1
- package/src/services/social-chat-history.service.ts +29 -25
- package/src/services/system-executor.service.ts +5 -9
- package/src/services/thread/thread-active-run.ts +2 -2
- package/src/services/thread/thread-listing.ts +61 -57
- package/src/services/thread/thread-memory-block.ts +73 -48
- package/src/services/thread/thread-message.service.ts +76 -65
- package/src/services/thread/thread-record-store.ts +8 -8
- package/src/services/thread/thread-title.service.ts +10 -4
- package/src/services/thread/thread-turn-execution.ts +43 -45
- package/src/services/thread/thread-turn-preparation.service.ts +257 -135
- package/src/services/thread/thread-turn-streaming.ts +82 -85
- package/src/services/thread/thread-turn.ts +8 -8
- package/src/services/thread/thread.service.ts +135 -100
- package/src/services/user.service.ts +45 -48
- package/src/storage/attachment-parser.ts +6 -2
- package/src/storage/attachment-storage.service.ts +5 -6
- package/src/storage/generated-document-storage.service.ts +1 -1
- package/src/system-agents/context-compaction.agent.ts +10 -9
- package/src/system-agents/delegated-agent-factory.ts +30 -6
- package/src/system-agents/memory-reranker.agent.ts +10 -9
- package/src/system-agents/memory.agent.ts +10 -9
- package/src/system-agents/recent-activity-title-refiner.agent.ts +13 -15
- package/src/system-agents/regular-chat-memory-digest.agent.ts +13 -12
- package/src/system-agents/skill-extractor.agent.ts +13 -12
- package/src/system-agents/skill-manager.agent.ts +13 -12
- package/src/system-agents/thread-router.agent.ts +10 -5
- package/src/system-agents/title-generator.agent.ts +13 -12
- package/src/tools/fetch-webpage.tool.ts +13 -13
- package/src/tools/memory-block.tool.ts +3 -1
- package/src/tools/plan-approval.tool.ts +4 -2
- package/src/tools/read-file-parts.tool.ts +10 -4
- package/src/tools/remember-memory.tool.ts +3 -1
- package/src/tools/research-topic.tool.ts +9 -5
- package/src/tools/search-web.tool.ts +16 -16
- package/src/tools/search.tool.ts +20 -5
- package/src/tools/team-think.tool.ts +61 -38
- package/src/utils/async.ts +5 -5
- package/src/utils/errors.ts +19 -18
- package/src/utils/sse-keepalive.ts +28 -25
- package/src/workers/bootstrap.ts +75 -11
- package/src/workers/memory-consolidation.worker.ts +82 -91
- package/src/workers/organization-learning.worker.ts +14 -4
- package/src/workers/regular-chat-memory-digest.runner.ts +105 -67
- package/src/workers/skill-extraction.runner.ts +97 -61
- package/src/workers/utils/repo-structure-extractor.ts +13 -8
- package/src/workers/utils/thread-message-query.ts +24 -24
- package/src/workers/worker-utils.ts +23 -4
- package/src/effect/helpers.ts +0 -123
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Effect, Schema } from 'effect'
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
|
+
import type { AiGatewayModels } from '../../ai-gateway/ai-gateway'
|
|
4
5
|
import { aiLogger } from '../../config/logger'
|
|
5
6
|
import type { MemorySearchResult } from '../../db/memory-types'
|
|
7
|
+
import { ERROR_TAGS } from '../../effect/errors'
|
|
6
8
|
import type { HelperModelRuntime } from '../../runtime/helper-model'
|
|
7
9
|
import type { MemoryRerankerStrategy, ResolvedLotaRuntimeConfig } from '../../runtime/runtime-config'
|
|
8
|
-
import {
|
|
10
|
+
import { makeMemoryRerankerAgentFactory, MEMORY_RERANKER_PROMPT } from '../../system-agents/memory-reranker.agent'
|
|
9
11
|
import { compactWhitespace, truncateText } from '../../utils/string'
|
|
10
12
|
import type { makeRerankService } from './rerank.service'
|
|
11
13
|
|
|
@@ -36,7 +38,7 @@ const RERANK_MULTI_SCOPE_PROMPT_SCHEMA = Schema.Struct({
|
|
|
36
38
|
const stringifyRerankPrompt = Schema.encodeSync(Schema.fromJsonString(RERANK_PROMPT_SCHEMA))
|
|
37
39
|
const stringifyRerankMultiScopePrompt = Schema.encodeSync(Schema.fromJsonString(RERANK_MULTI_SCOPE_PROMPT_SCHEMA))
|
|
38
40
|
|
|
39
|
-
class MemoryRerankError extends Schema.TaggedErrorClass<MemoryRerankError>()(
|
|
41
|
+
class MemoryRerankError extends Schema.TaggedErrorClass<MemoryRerankError>()(ERROR_TAGS.MemoryRerankError, {
|
|
40
42
|
scope: Schema.Literals(['single', 'multi-scope']),
|
|
41
43
|
message: Schema.String,
|
|
42
44
|
cause: Schema.optional(Schema.Unknown),
|
|
@@ -119,6 +121,7 @@ function logRerankError(scope: 'single' | 'multi-scope', error: unknown): void {
|
|
|
119
121
|
|
|
120
122
|
function rerankCandidatesWithHelperEffect(
|
|
121
123
|
helperModelRuntime: HelperModelRuntime,
|
|
124
|
+
aiGatewayModels: AiGatewayModels,
|
|
122
125
|
query: string,
|
|
123
126
|
candidates: MemorySearchResult[],
|
|
124
127
|
maxItems: number,
|
|
@@ -127,7 +130,7 @@ function rerankCandidatesWithHelperEffect(
|
|
|
127
130
|
try: () =>
|
|
128
131
|
helperModelRuntime.generateHelperStructured({
|
|
129
132
|
tag: 'memory-reranker',
|
|
130
|
-
createAgent:
|
|
133
|
+
createAgent: makeMemoryRerankerAgentFactory(aiGatewayModels),
|
|
131
134
|
defaultSystemPrompt: MEMORY_RERANKER_PROMPT,
|
|
132
135
|
messages: [
|
|
133
136
|
{
|
|
@@ -179,6 +182,7 @@ function rerankCandidatesWithRerankServiceEffect(
|
|
|
179
182
|
export function rerankCandidates(
|
|
180
183
|
runtimeConfig: ResolvedLotaRuntimeConfig,
|
|
181
184
|
helperModelRuntime: HelperModelRuntime,
|
|
185
|
+
aiGatewayModels: AiGatewayModels,
|
|
182
186
|
query: string,
|
|
183
187
|
candidates: MemorySearchResult[],
|
|
184
188
|
maxItems: number,
|
|
@@ -191,7 +195,7 @@ export function rerankCandidates(
|
|
|
191
195
|
return yield* rerankCandidatesWithRerankServiceEffect(query, candidates, maxItems, reranker)
|
|
192
196
|
}
|
|
193
197
|
|
|
194
|
-
return yield* rerankCandidatesWithHelperEffect(helperModelRuntime, query, candidates, maxItems)
|
|
198
|
+
return yield* rerankCandidatesWithHelperEffect(helperModelRuntime, aiGatewayModels, query, candidates, maxItems)
|
|
195
199
|
}).pipe(
|
|
196
200
|
Effect.catchCause((cause) =>
|
|
197
201
|
Effect.sync(() => {
|
|
@@ -204,6 +208,7 @@ export function rerankCandidates(
|
|
|
204
208
|
|
|
205
209
|
function rerankCandidatesMultiScopeWithHelperEffect(
|
|
206
210
|
helperModelRuntime: HelperModelRuntime,
|
|
211
|
+
aiGatewayModels: AiGatewayModels,
|
|
207
212
|
query: string,
|
|
208
213
|
flattenedCandidates: Array<MemorySearchResult & { scopeTag: string }>,
|
|
209
214
|
maxItems: number,
|
|
@@ -212,7 +217,7 @@ function rerankCandidatesMultiScopeWithHelperEffect(
|
|
|
212
217
|
try: () =>
|
|
213
218
|
helperModelRuntime.generateHelperStructured({
|
|
214
219
|
tag: 'memory-reranker-multi-scope',
|
|
215
|
-
createAgent:
|
|
220
|
+
createAgent: makeMemoryRerankerAgentFactory(aiGatewayModels),
|
|
216
221
|
defaultSystemPrompt: MEMORY_RERANKER_PROMPT,
|
|
217
222
|
messages: [
|
|
218
223
|
{
|
|
@@ -270,6 +275,7 @@ function rerankCandidatesMultiScopeWithRerankServiceEffect(
|
|
|
270
275
|
export function rerankCandidatesMultiScope(
|
|
271
276
|
runtimeConfig: ResolvedLotaRuntimeConfig,
|
|
272
277
|
helperModelRuntime: HelperModelRuntime,
|
|
278
|
+
aiGatewayModels: AiGatewayModels,
|
|
273
279
|
query: string,
|
|
274
280
|
scopedCandidates: Array<{ scopeTag: string; candidates: MemorySearchResult[] }>,
|
|
275
281
|
maxItems: number,
|
|
@@ -285,7 +291,13 @@ export function rerankCandidatesMultiScope(
|
|
|
285
291
|
return yield* rerankCandidatesMultiScopeWithRerankServiceEffect(query, flattened, maxItems, reranker)
|
|
286
292
|
}
|
|
287
293
|
|
|
288
|
-
return yield* rerankCandidatesMultiScopeWithHelperEffect(
|
|
294
|
+
return yield* rerankCandidatesMultiScopeWithHelperEffect(
|
|
295
|
+
helperModelRuntime,
|
|
296
|
+
aiGatewayModels,
|
|
297
|
+
query,
|
|
298
|
+
flattened,
|
|
299
|
+
maxItems,
|
|
300
|
+
)
|
|
289
301
|
}).pipe(
|
|
290
302
|
Effect.catchCause((cause) =>
|
|
291
303
|
Effect.sync(() => {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Context, Schema, Effect, Layer } from 'effect'
|
|
2
2
|
|
|
3
|
+
import { AiGatewayModelsTag, RuntimeBridgeTag } from '../../ai-gateway/ai-gateway'
|
|
4
|
+
import type { AiGatewayModels } from '../../ai-gateway/ai-gateway'
|
|
3
5
|
import type { ResolvedAgentConfig } from '../../config/agent-defaults'
|
|
4
6
|
import { aiLogger } from '../../config/logger'
|
|
5
7
|
import type { Memory } from '../../db/memory'
|
|
@@ -13,6 +15,7 @@ import type {
|
|
|
13
15
|
MemoryType,
|
|
14
16
|
RelationType,
|
|
15
17
|
} from '../../db/memory-types'
|
|
18
|
+
import { ERROR_TAGS } from '../../effect/errors'
|
|
16
19
|
import { AgentConfigServiceTag, DatabaseServiceTag, RuntimeConfigServiceTag } from '../../effect/services'
|
|
17
20
|
import { withOrgMemoryLockEffect } from '../../redis/org-memory-lock'
|
|
18
21
|
import type { HelperModelRuntime } from '../../runtime/helper-model'
|
|
@@ -25,9 +28,8 @@ import {
|
|
|
25
28
|
} from '../../runtime/retrieval-adapters'
|
|
26
29
|
import type { ResolvedLotaRuntimeConfig } from '../../runtime/runtime-config'
|
|
27
30
|
import { clampImportance, compactWhitespace } from '../../utils/string'
|
|
28
|
-
import {
|
|
29
|
-
import
|
|
30
|
-
import { tryMemoryPromise as memoryTryPromise } from './memory-errors'
|
|
31
|
+
import { BackgroundWorkServiceTag } from '../background-work.service'
|
|
32
|
+
import { MemoryServiceError } from './memory-errors'
|
|
31
33
|
import type { MemoryRerankOutput } from './memory-rerank'
|
|
32
34
|
import { formatMemoryResults, formatRerankedResults, getCandidateLimit } from './memory-utils'
|
|
33
35
|
export type { MemoryRerankOutput } from './memory-rerank'
|
|
@@ -52,7 +54,7 @@ const ONBOARDING_MEMORY_EXTRACTION_PROMPT =
|
|
|
52
54
|
const DIRECT_MEMORY_ASSESSMENT_PROMPT =
|
|
53
55
|
'The user is submitting a direct memory candidate. Keep the wording faithful. Return one fact only when the statement is durable enough for memory; otherwise return no facts.'
|
|
54
56
|
|
|
55
|
-
class InvalidAgentNameError extends Schema.TaggedErrorClass<InvalidAgentNameError>()(
|
|
57
|
+
class InvalidAgentNameError extends Schema.TaggedErrorClass<InvalidAgentNameError>()(ERROR_TAGS.InvalidAgentNameError, {
|
|
56
58
|
agentName: Schema.String,
|
|
57
59
|
}) {}
|
|
58
60
|
|
|
@@ -61,6 +63,7 @@ type EffectSuccess<T> = T extends Effect.Effect<infer A, infer _E, infer _R> ? A
|
|
|
61
63
|
function searchMemoriesEffect({
|
|
62
64
|
runtimeConfig,
|
|
63
65
|
helperModelRuntime,
|
|
66
|
+
aiGatewayModels,
|
|
64
67
|
resolveReranker,
|
|
65
68
|
query,
|
|
66
69
|
memory,
|
|
@@ -70,6 +73,7 @@ function searchMemoriesEffect({
|
|
|
70
73
|
}: {
|
|
71
74
|
runtimeConfig: ResolvedLotaRuntimeConfig
|
|
72
75
|
helperModelRuntime: HelperModelRuntime
|
|
76
|
+
aiGatewayModels: AiGatewayModels
|
|
73
77
|
resolveReranker: () => ReturnType<typeof makeRerankService>
|
|
74
78
|
query: string
|
|
75
79
|
memory: Memory
|
|
@@ -80,15 +84,17 @@ function searchMemoriesEffect({
|
|
|
80
84
|
return Effect.gen(function* () {
|
|
81
85
|
const limit = runtimeConfig.memory.searchK
|
|
82
86
|
const candidateLimit = fastMode ? limit : getCandidateLimit(limit)
|
|
83
|
-
const candidates = yield*
|
|
84
|
-
|
|
87
|
+
const candidates = yield* memory
|
|
88
|
+
.searchCandidates(query, {
|
|
85
89
|
scopeId,
|
|
86
90
|
limit: candidateLimit,
|
|
87
91
|
memoryType,
|
|
88
92
|
fastMode,
|
|
89
93
|
includeNeighborContext: !fastMode,
|
|
90
|
-
})
|
|
91
|
-
|
|
94
|
+
})
|
|
95
|
+
.pipe(
|
|
96
|
+
Effect.mapError((cause) => new MemoryServiceError({ message: 'Failed to search memory candidates.', cause })),
|
|
97
|
+
)
|
|
92
98
|
|
|
93
99
|
aiLogger.debug`Memory search candidates (scopeId: ${scopeId}, candidates: ${candidates.length})`
|
|
94
100
|
|
|
@@ -104,6 +110,7 @@ function searchMemoriesEffect({
|
|
|
104
110
|
const reranked = yield* rerankCandidates(
|
|
105
111
|
runtimeConfig,
|
|
106
112
|
helperModelRuntime,
|
|
113
|
+
aiGatewayModels,
|
|
107
114
|
query,
|
|
108
115
|
candidates,
|
|
109
116
|
limit,
|
|
@@ -125,16 +132,24 @@ function applyPreparedScopeUpdatesEffect(params: {
|
|
|
125
132
|
}
|
|
126
133
|
|
|
127
134
|
if (params.acquireLock === false) {
|
|
128
|
-
yield*
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
yield* params.orgMemory
|
|
136
|
+
.applyPreparedScopeUpdates(params.preparedUpdates)
|
|
137
|
+
.pipe(
|
|
138
|
+
Effect.mapError(
|
|
139
|
+
(cause) => new MemoryServiceError({ message: 'Failed to apply prepared memory scope updates.', cause }),
|
|
140
|
+
),
|
|
141
|
+
)
|
|
131
142
|
return
|
|
132
143
|
}
|
|
133
144
|
|
|
134
145
|
yield* withOrgMemoryLockEffect(params.orgId, () =>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
146
|
+
params.orgMemory
|
|
147
|
+
.applyPreparedScopeUpdates(params.preparedUpdates)
|
|
148
|
+
.pipe(
|
|
149
|
+
Effect.mapError(
|
|
150
|
+
(cause) => new MemoryServiceError({ message: 'Failed to apply prepared memory scope updates.', cause }),
|
|
151
|
+
),
|
|
152
|
+
),
|
|
138
153
|
)
|
|
139
154
|
})
|
|
140
155
|
}
|
|
@@ -179,10 +194,11 @@ interface MemoryServiceDeps {
|
|
|
179
194
|
helperModelRuntime: HelperModelRuntime
|
|
180
195
|
orgMemoryCache: OrgMemoryCache
|
|
181
196
|
agentConfig: ResolvedAgentConfig
|
|
197
|
+
aiGatewayModels: AiGatewayModels
|
|
182
198
|
}
|
|
183
199
|
|
|
184
200
|
export function createMemoryService(deps: MemoryServiceDeps) {
|
|
185
|
-
const { runtimeConfig, helperModelRuntime, orgMemoryCache, agentConfig } = deps
|
|
201
|
+
const { runtimeConfig, helperModelRuntime, orgMemoryCache, agentConfig, aiGatewayModels } = deps
|
|
186
202
|
const resolveRerankService = () => deps.rerankService
|
|
187
203
|
const service = {
|
|
188
204
|
searchOrganizationMemories: Effect.fn('MemoryService.searchOrganizationMemories')(function* (
|
|
@@ -195,6 +211,7 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
195
211
|
const results = yield* searchMemoriesEffect({
|
|
196
212
|
runtimeConfig,
|
|
197
213
|
helperModelRuntime,
|
|
214
|
+
aiGatewayModels,
|
|
198
215
|
resolveReranker: resolveRerankService,
|
|
199
216
|
query,
|
|
200
217
|
memory,
|
|
@@ -205,24 +222,20 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
205
222
|
return results
|
|
206
223
|
}),
|
|
207
224
|
|
|
208
|
-
getStaleMemories(orgId: string) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}).pipe(
|
|
214
|
-
Effect.withSpan('MemoryService.getStaleMemories'),
|
|
215
|
-
Effect.map((stale) => {
|
|
216
|
-
if (stale.length === 0) return ''
|
|
217
|
-
const items = stale.map((m) => `- [NEEDS REVIEW] ${m.content}`).join('\n')
|
|
218
|
-
return `Memories flagged for review (parent fact was superseded):\n${items}`
|
|
219
|
-
}),
|
|
225
|
+
getStaleMemories: Effect.fn('MemoryService.getStaleMemories')(function* (orgId: string) {
|
|
226
|
+
const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
|
|
227
|
+
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
228
|
+
const stale = yield* memory.getStaleMemories(orgScopeId).pipe(
|
|
229
|
+
Effect.mapError((cause) => new MemoryServiceError({ message: 'Failed to load stale memories.', cause })),
|
|
220
230
|
Effect.catch((error) => {
|
|
221
231
|
aiLogger.error`Failed to get stale memories: ${error.cause}`
|
|
222
|
-
return Effect.succeed(
|
|
232
|
+
return Effect.succeed<MemorySearchResult[]>([])
|
|
223
233
|
}),
|
|
224
234
|
)
|
|
225
|
-
|
|
235
|
+
if (stale.length === 0) return ''
|
|
236
|
+
const items = stale.map((m) => `- [NEEDS REVIEW] ${m.content}`).join('\n')
|
|
237
|
+
return `Memories flagged for review (parent fact was superseded):\n${items}`
|
|
238
|
+
}),
|
|
226
239
|
|
|
227
240
|
searchOrganizationMemoriesRaw: Effect.fn('MemoryService.searchOrganizationMemoriesRaw')(function* (
|
|
228
241
|
orgId: string,
|
|
@@ -235,15 +248,19 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
235
248
|
const fastMode = options?.fastMode ?? true
|
|
236
249
|
const searchK = runtimeConfig.memory.searchK
|
|
237
250
|
const limit = options?.limit ?? (fastMode ? Math.min(searchK, 4) : searchK)
|
|
238
|
-
const candidates = yield*
|
|
239
|
-
|
|
251
|
+
const candidates = yield* memory
|
|
252
|
+
.searchCandidates(query, {
|
|
240
253
|
scopeId: orgScopeId,
|
|
241
254
|
limit,
|
|
242
255
|
memoryType: ORG_MEMORY_TYPE,
|
|
243
256
|
fastMode,
|
|
244
257
|
includeNeighborContext: !fastMode,
|
|
245
|
-
})
|
|
246
|
-
|
|
258
|
+
})
|
|
259
|
+
.pipe(
|
|
260
|
+
Effect.mapError(
|
|
261
|
+
(cause) => new MemoryServiceError({ message: 'Failed to search organization memories.', cause }),
|
|
262
|
+
),
|
|
263
|
+
)
|
|
247
264
|
aiLogger.debug`Organization memory search (raw) completed (candidates: ${candidates.length})`
|
|
248
265
|
return formatMemoryResults(candidates)
|
|
249
266
|
}),
|
|
@@ -264,6 +281,7 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
264
281
|
const results = yield* searchMemoriesEffect({
|
|
265
282
|
runtimeConfig,
|
|
266
283
|
helperModelRuntime,
|
|
284
|
+
aiGatewayModels,
|
|
267
285
|
resolveReranker: resolveRerankService,
|
|
268
286
|
query,
|
|
269
287
|
memory,
|
|
@@ -285,9 +303,13 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
285
303
|
const { orgId, ...listOptions } = params
|
|
286
304
|
const orgMemory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
287
305
|
const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
|
|
288
|
-
return yield*
|
|
289
|
-
|
|
290
|
-
|
|
306
|
+
return yield* orgMemory
|
|
307
|
+
.list({ scopeId: orgScopeId, ...listOptions })
|
|
308
|
+
.pipe(
|
|
309
|
+
Effect.mapError(
|
|
310
|
+
(cause) => new MemoryServiceError({ message: 'Failed to list organization memory records.', cause }),
|
|
311
|
+
),
|
|
312
|
+
)
|
|
291
313
|
}),
|
|
292
314
|
|
|
293
315
|
getTopMemories(params: { orgId: string; agentName?: string; limit?: number }) {
|
|
@@ -346,8 +368,10 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
346
368
|
})
|
|
347
369
|
}
|
|
348
370
|
|
|
349
|
-
const results = yield*
|
|
350
|
-
|
|
371
|
+
const results = yield* executeScopedRetrieval(retrievalTasks).pipe(
|
|
372
|
+
Effect.mapError(
|
|
373
|
+
(cause) => new MemoryServiceError({ message: 'Failed to execute scoped memory retrieval.', cause }),
|
|
374
|
+
),
|
|
351
375
|
)
|
|
352
376
|
const totalCandidates = countScopedRetrievalCandidates(results)
|
|
353
377
|
aiLogger.debug`Batched memory search candidates (scopes: ${results.length}, total: ${totalCandidates})`
|
|
@@ -364,6 +388,7 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
364
388
|
const reranked = yield* rerankCandidatesMultiScope(
|
|
365
389
|
runtimeConfig,
|
|
366
390
|
helperModelRuntime,
|
|
391
|
+
aiGatewayModels,
|
|
367
392
|
query,
|
|
368
393
|
results,
|
|
369
394
|
limit,
|
|
@@ -372,7 +397,7 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
372
397
|
return formatBatchedResults(reranked, candidatesByScopeTag, limit, agentName)
|
|
373
398
|
}),
|
|
374
399
|
|
|
375
|
-
createOrganizationMemory({
|
|
400
|
+
createOrganizationMemory: Effect.fn('MemoryService.createOrganizationMemory')(function* ({
|
|
376
401
|
orgId,
|
|
377
402
|
content,
|
|
378
403
|
memoryType,
|
|
@@ -387,35 +412,30 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
387
412
|
importance?: number
|
|
388
413
|
durability?: MemoryRecord['durability']
|
|
389
414
|
}) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
415
|
+
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
416
|
+
const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
|
|
417
|
+
aiLogger.debug`createOrganizationMemory - orgId: "${orgId}", scopeId: "${orgScopeId}", content preview: "${content.slice(0, 50)}"`
|
|
418
|
+
return yield* memory
|
|
419
|
+
.insert(content, {
|
|
420
|
+
scopeId: orgScopeId,
|
|
421
|
+
memoryType,
|
|
422
|
+
importance: importance ?? 1,
|
|
423
|
+
durability,
|
|
424
|
+
metadata: { orgId, ...metadata },
|
|
425
|
+
})
|
|
426
|
+
.pipe(
|
|
427
|
+
Effect.mapError(
|
|
428
|
+
(cause) => new MemoryServiceError({ message: 'Failed to create organization memory.', cause }),
|
|
429
|
+
),
|
|
430
|
+
Effect.catch((error) => {
|
|
431
|
+
if (isUniqueIndexConflict(error, 'memoryHashIdx')) {
|
|
432
|
+
aiLogger.debug`Organization memory already exists (hash conflict)`
|
|
433
|
+
return Effect.succeed('')
|
|
434
|
+
}
|
|
435
|
+
return Effect.fail(error)
|
|
400
436
|
}),
|
|
401
437
|
)
|
|
402
|
-
|
|
403
|
-
Effect.withSpan('MemoryService.createOrganizationMemory'),
|
|
404
|
-
Effect.tap(() =>
|
|
405
|
-
Effect.gen(function* () {
|
|
406
|
-
const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
|
|
407
|
-
aiLogger.debug`createOrganizationMemory - orgId: "${orgId}", scopeId: "${orgScopeId}", content preview: "${content.slice(0, 50)}"`
|
|
408
|
-
}),
|
|
409
|
-
),
|
|
410
|
-
Effect.catch((error) => {
|
|
411
|
-
if (isUniqueIndexConflict(error, 'memoryHashIdx')) {
|
|
412
|
-
aiLogger.debug`Organization memory already exists (hash conflict)`
|
|
413
|
-
return Effect.succeed('')
|
|
414
|
-
}
|
|
415
|
-
return Effect.fail(error)
|
|
416
|
-
}),
|
|
417
|
-
)
|
|
418
|
-
},
|
|
438
|
+
}),
|
|
419
439
|
|
|
420
440
|
addOrganizationMemoryRelation: Effect.fn('MemoryService.addOrganizationMemoryRelation')(function* ({
|
|
421
441
|
orgId,
|
|
@@ -431,12 +451,16 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
431
451
|
confidence?: number
|
|
432
452
|
}) {
|
|
433
453
|
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
434
|
-
return yield*
|
|
435
|
-
|
|
436
|
-
|
|
454
|
+
return yield* memory
|
|
455
|
+
.addRelation(fromMemoryId, toMemoryId, relationType, confidence)
|
|
456
|
+
.pipe(
|
|
457
|
+
Effect.mapError(
|
|
458
|
+
(cause) => new MemoryServiceError({ message: 'Failed to add organization memory relation.', cause }),
|
|
459
|
+
),
|
|
460
|
+
)
|
|
437
461
|
}),
|
|
438
462
|
|
|
439
|
-
createAgentMemory({
|
|
463
|
+
createAgentMemory: Effect.fn('MemoryService.createAgentMemory')(function* ({
|
|
440
464
|
orgId,
|
|
441
465
|
agentName,
|
|
442
466
|
content,
|
|
@@ -452,31 +476,28 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
452
476
|
importance?: number
|
|
453
477
|
}) {
|
|
454
478
|
if (!isRoutableAgentName(agentConfig, agentName)) {
|
|
455
|
-
return
|
|
479
|
+
return yield* new InvalidAgentNameError({ agentName })
|
|
456
480
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
481
|
+
const scoped = yield* agentScopeId(orgId, agentName)
|
|
482
|
+
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
483
|
+
return yield* memory
|
|
484
|
+
.insert(content, {
|
|
485
|
+
scopeId: scoped,
|
|
486
|
+
memoryType,
|
|
487
|
+
importance: importance ?? 1,
|
|
488
|
+
metadata: { orgId, agentName, memoryScope: 'agent', ...metadata },
|
|
489
|
+
})
|
|
490
|
+
.pipe(
|
|
491
|
+
Effect.mapError((cause) => new MemoryServiceError({ message: 'Failed to create agent memory.', cause })),
|
|
492
|
+
Effect.catch((error) => {
|
|
493
|
+
if (isUniqueIndexConflict(error, 'memoryHashIdx')) {
|
|
494
|
+
aiLogger.debug`Agent memory already exists (hash conflict)`
|
|
495
|
+
return Effect.succeed('')
|
|
496
|
+
}
|
|
497
|
+
return Effect.fail(error)
|
|
467
498
|
}),
|
|
468
499
|
)
|
|
469
|
-
|
|
470
|
-
Effect.withSpan('MemoryService.createAgentMemory'),
|
|
471
|
-
Effect.catch((error) => {
|
|
472
|
-
if (isUniqueIndexConflict(error, 'memoryHashIdx')) {
|
|
473
|
-
aiLogger.debug`Agent memory already exists (hash conflict)`
|
|
474
|
-
return Effect.succeed('')
|
|
475
|
-
}
|
|
476
|
-
return Effect.fail(error)
|
|
477
|
-
}),
|
|
478
|
-
)
|
|
479
|
-
},
|
|
500
|
+
}),
|
|
480
501
|
|
|
481
502
|
assessMemoryCandidate: Effect.fn('MemoryService.assessMemoryCandidate')(function* (params: {
|
|
482
503
|
orgId: string
|
|
@@ -486,12 +507,16 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
486
507
|
if (!trimmed) return null
|
|
487
508
|
|
|
488
509
|
const memory = yield* getOrgMemory(orgMemoryCache, params.orgId)
|
|
489
|
-
const facts = yield*
|
|
490
|
-
|
|
510
|
+
const facts = yield* memory
|
|
511
|
+
.extractFactsFromMessages([{ role: 'user', content: trimmed }], {
|
|
491
512
|
maxFacts: 1,
|
|
492
513
|
customPrompt: DIRECT_MEMORY_ASSESSMENT_PROMPT,
|
|
493
|
-
})
|
|
494
|
-
|
|
514
|
+
})
|
|
515
|
+
.pipe(
|
|
516
|
+
Effect.mapError(
|
|
517
|
+
(cause) => new MemoryServiceError({ message: 'Failed to assess direct memory candidate.', cause }),
|
|
518
|
+
),
|
|
519
|
+
)
|
|
495
520
|
if (facts.length === 0) return null
|
|
496
521
|
const fact = facts[0]
|
|
497
522
|
|
|
@@ -513,9 +538,13 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
513
538
|
content: string
|
|
514
539
|
}) {
|
|
515
540
|
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
516
|
-
return yield*
|
|
517
|
-
|
|
518
|
-
|
|
541
|
+
return yield* memory
|
|
542
|
+
.updateMemory(memoryId, content)
|
|
543
|
+
.pipe(
|
|
544
|
+
Effect.mapError(
|
|
545
|
+
(cause) => new MemoryServiceError({ message: 'Failed to update organization memory.', cause }),
|
|
546
|
+
),
|
|
547
|
+
)
|
|
519
548
|
}),
|
|
520
549
|
|
|
521
550
|
addExtractedFactsToScopes: Effect.fn('MemoryService.addExtractedFactsToScopes')(function* (params: {
|
|
@@ -553,9 +582,14 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
553
582
|
})
|
|
554
583
|
}
|
|
555
584
|
|
|
556
|
-
const preparedUpdates = yield*
|
|
557
|
-
|
|
558
|
-
|
|
585
|
+
const preparedUpdates = yield* orgMemory
|
|
586
|
+
.prepareFactsToScopes(params.facts, scopes)
|
|
587
|
+
.pipe(
|
|
588
|
+
Effect.mapError(
|
|
589
|
+
(cause) =>
|
|
590
|
+
new MemoryServiceError({ message: 'Failed to prepare extracted facts for memory scopes.', cause }),
|
|
591
|
+
),
|
|
592
|
+
)
|
|
559
593
|
yield* applyPreparedScopeUpdatesEffect({
|
|
560
594
|
orgId: params.orgId,
|
|
561
595
|
orgMemory,
|
|
@@ -637,17 +671,26 @@ export function createMemoryService(deps: MemoryServiceDeps) {
|
|
|
637
671
|
})
|
|
638
672
|
}
|
|
639
673
|
|
|
640
|
-
const extractedFacts = yield*
|
|
641
|
-
|
|
642
|
-
|
|
674
|
+
const extractedFacts = yield* orgMemory
|
|
675
|
+
.extractFactsFromMessages(messages, extractionConfig)
|
|
676
|
+
.pipe(
|
|
677
|
+
Effect.mapError(
|
|
678
|
+
(cause) => new MemoryServiceError({ message: 'Failed to extract conversation memories.', cause }),
|
|
679
|
+
),
|
|
680
|
+
)
|
|
643
681
|
if (shouldSkipExtractedFacts(extractedFacts)) {
|
|
644
682
|
aiLogger.debug`Skipping transient conversation memory`
|
|
645
683
|
return
|
|
646
684
|
}
|
|
647
685
|
|
|
648
|
-
const preparedUpdates = yield*
|
|
649
|
-
|
|
650
|
-
|
|
686
|
+
const preparedUpdates = yield* orgMemory
|
|
687
|
+
.prepareFactsToScopes(extractedFacts, scopes)
|
|
688
|
+
.pipe(
|
|
689
|
+
Effect.mapError(
|
|
690
|
+
(cause) =>
|
|
691
|
+
new MemoryServiceError({ message: 'Failed to prepare conversation memories for scopes.', cause }),
|
|
692
|
+
),
|
|
693
|
+
)
|
|
651
694
|
yield* applyPreparedScopeUpdatesEffect({ orgId, orgMemory, preparedUpdates })
|
|
652
695
|
if (preparedUpdates.length > 0) {
|
|
653
696
|
aiLogger.debug`Conversation memories added to ${scopes.length} scope(s) from ${messages.length} message(s)`
|
|
@@ -669,9 +712,25 @@ export const MemoryServiceLive = Layer.effect(
|
|
|
669
712
|
const runtimeConfig = yield* RuntimeConfigServiceTag
|
|
670
713
|
const rerankService = yield* RerankServiceTag
|
|
671
714
|
const helperModelRuntime = yield* HelperModelTag
|
|
672
|
-
const background = yield*
|
|
715
|
+
const background = yield* BackgroundWorkServiceTag
|
|
673
716
|
const agentConfig = yield* AgentConfigServiceTag
|
|
674
|
-
const
|
|
675
|
-
|
|
717
|
+
const aiGatewayModels = yield* AiGatewayModelsTag
|
|
718
|
+
const bridge = yield* RuntimeBridgeTag
|
|
719
|
+
const orgMemoryCache = yield* makeOrgMemoryCache({
|
|
720
|
+
db,
|
|
721
|
+
runtimeConfig,
|
|
722
|
+
helperModelRuntime,
|
|
723
|
+
background,
|
|
724
|
+
aiGatewayModels,
|
|
725
|
+
runPromise: bridge.runPromise,
|
|
726
|
+
})
|
|
727
|
+
return createMemoryService({
|
|
728
|
+
runtimeConfig,
|
|
729
|
+
rerankService,
|
|
730
|
+
helperModelRuntime,
|
|
731
|
+
orgMemoryCache,
|
|
732
|
+
agentConfig,
|
|
733
|
+
aiGatewayModels,
|
|
734
|
+
})
|
|
676
735
|
}),
|
|
677
736
|
)
|
|
@@ -3,7 +3,7 @@ import * as Schema from 'effect/Schema'
|
|
|
3
3
|
import { z } from 'zod'
|
|
4
4
|
|
|
5
5
|
import { OPENROUTER_FAST_RERANK_MODEL_ID } from '../../config/model-constants'
|
|
6
|
-
import {
|
|
6
|
+
import { ERROR_TAGS } from '../../effect/errors'
|
|
7
7
|
import { RuntimeConfigServiceTag } from '../../effect/services'
|
|
8
8
|
import { toValidationError } from '../../effect/zod'
|
|
9
9
|
import { normalizeDirectOpenRouterModelId, resolveOpenRouterApiKey } from '../../openrouter/direct-provider'
|
|
@@ -11,7 +11,16 @@ import type { ResolvedLotaRuntimeConfig } from '../../runtime/runtime-config'
|
|
|
11
11
|
|
|
12
12
|
const OPENROUTER_RERANK_URL = 'https://openrouter.ai/api/v1/rerank' as const
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
const RerankRequestBodySchema = Schema.Struct({
|
|
15
|
+
model: Schema.String,
|
|
16
|
+
query: Schema.String,
|
|
17
|
+
documents: Schema.Array(Schema.String),
|
|
18
|
+
top_n: Schema.Number,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const encodeRerankRequestBody = Schema.encodeSync(Schema.fromJsonString(RerankRequestBodySchema))
|
|
22
|
+
|
|
23
|
+
class RerankServiceError extends Schema.TaggedErrorClass<RerankServiceError>()(ERROR_TAGS.RerankServiceError, {
|
|
15
24
|
operation: Schema.String,
|
|
16
25
|
message: Schema.String,
|
|
17
26
|
cause: Schema.Defect,
|
|
@@ -21,8 +30,6 @@ function toRerankServiceError(operation: string, message: string, cause: unknown
|
|
|
21
30
|
return new RerankServiceError({ operation, message, cause })
|
|
22
31
|
}
|
|
23
32
|
|
|
24
|
-
const tryRerankPromise = makeEffectTryPromiseWithOperation(toRerankServiceError)
|
|
25
|
-
|
|
26
33
|
const RerankResponseSchema = z
|
|
27
34
|
.object({
|
|
28
35
|
model: z.string().optional(),
|
|
@@ -112,25 +119,27 @@ export function makeRerankService(config: ResolvedLotaRuntimeConfig) {
|
|
|
112
119
|
const apiKey = resolveOpenRouterApiKey(config.aiGateway.openRouterApiKey)
|
|
113
120
|
const modelId = resolveRerankModelId(params.modelId)
|
|
114
121
|
const topN = clampTopN(params.topN, params.documents.length)
|
|
122
|
+
const requestBody = encodeRerankRequestBody({
|
|
123
|
+
model: modelId,
|
|
124
|
+
query: params.query,
|
|
125
|
+
documents: params.documents.map((document) => document.text),
|
|
126
|
+
top_n: topN,
|
|
127
|
+
})
|
|
115
128
|
|
|
116
|
-
const response = yield*
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
query: params.query,
|
|
123
|
-
documents: params.documents.map((document) => document.text),
|
|
124
|
-
top_n: topN,
|
|
129
|
+
const response = yield* Effect.tryPromise({
|
|
130
|
+
try: () =>
|
|
131
|
+
Bun.fetch(OPENROUTER_RERANK_URL, {
|
|
132
|
+
method: 'POST',
|
|
133
|
+
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
134
|
+
body: requestBody,
|
|
125
135
|
}),
|
|
126
|
-
|
|
127
|
-
)
|
|
136
|
+
catch: (cause) => toRerankServiceError('fetch-rerank', 'Failed to fetch rerank results.', cause),
|
|
137
|
+
})
|
|
128
138
|
|
|
129
|
-
const responseText = yield*
|
|
130
|
-
|
|
131
|
-
'Failed to read rerank response body.',
|
|
132
|
-
|
|
133
|
-
)
|
|
139
|
+
const responseText = yield* Effect.tryPromise({
|
|
140
|
+
try: () => response.text(),
|
|
141
|
+
catch: (cause) => toRerankServiceError('read-rerank-response', 'Failed to read rerank response body.', cause),
|
|
142
|
+
})
|
|
134
143
|
|
|
135
144
|
if (!response.ok) {
|
|
136
145
|
return yield* new RerankServiceError({
|
|
@@ -29,7 +29,7 @@ type SdkOrganizationMemberRecord = z.infer<typeof organizationMemberRecordSchema
|
|
|
29
29
|
export type SdkOrganizationMember = z.infer<typeof sdkOrganizationMemberSchema>
|
|
30
30
|
|
|
31
31
|
class OrganizationMemberServiceError extends Schema.TaggedErrorClass<OrganizationMemberServiceError>()(
|
|
32
|
-
'OrganizationMemberServiceError',
|
|
32
|
+
'@lota-sdk/core/OrganizationMemberServiceError',
|
|
33
33
|
{ operation: Schema.String, cause: Schema.Defect },
|
|
34
34
|
) {}
|
|
35
35
|
|