@lota-sdk/core 0.4.8 → 0.4.9
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 +94 -22
- package/src/ai-gateway/ai-gateway.ts +738 -223
- package/src/config/agent-defaults.ts +176 -75
- package/src/config/agent-types.ts +54 -4
- package/src/config/constants.ts +8 -2
- package/src/config/logger.ts +286 -19
- package/src/config/thread-defaults.ts +33 -21
- package/src/create-runtime.ts +725 -387
- package/src/db/base.service.ts +52 -28
- package/src/db/cursor-pagination.ts +71 -30
- package/src/db/memory-store.helpers.ts +4 -7
- package/src/db/memory-store.ts +856 -598
- package/src/db/memory.ts +398 -275
- package/src/db/record-id.ts +32 -10
- package/src/db/schema-fingerprint.ts +30 -12
- package/src/db/service-normalization.ts +255 -0
- package/src/db/service.ts +726 -761
- package/src/db/startup.ts +140 -66
- package/src/db/transaction-conflict.ts +15 -0
- package/src/effect/awaitable-effect.ts +87 -0
- package/src/effect/errors.ts +121 -0
- package/src/effect/helpers.ts +98 -0
- package/src/effect/index.ts +22 -0
- package/src/effect/layers.ts +228 -0
- package/src/effect/runtime-ref.ts +25 -0
- package/src/effect/runtime.ts +31 -0
- package/src/effect/services.ts +57 -0
- package/src/effect/zod.ts +43 -0
- package/src/embeddings/provider.ts +122 -76
- package/src/index.ts +46 -1
- package/src/openrouter/direct-provider.ts +11 -35
- package/src/queues/autonomous-job.queue.ts +130 -74
- package/src/queues/context-compaction.queue.ts +60 -15
- package/src/queues/delayed-node-promotion.queue.ts +52 -15
- 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 +13 -4
- package/src/queues/plan-agent-heartbeat.queue.ts +65 -21
- package/src/queues/plan-scheduler.queue.ts +107 -31
- package/src/queues/post-chat-memory.queue.ts +66 -24
- package/src/queues/queue-factory.ts +142 -52
- package/src/queues/standalone-worker.ts +39 -0
- package/src/queues/title-generation.queue.ts +54 -9
- package/src/redis/connection.ts +84 -32
- 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 +10 -0
- package/src/redis/stream-context.ts +84 -46
- package/src/runtime/agent-identity-overrides.ts +2 -2
- package/src/runtime/agent-runtime-policy.ts +4 -1
- package/src/runtime/agent-stream-helpers.ts +20 -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} +114 -91
- package/src/runtime/execution-plan-visibility.ts +2 -2
- package/src/runtime/execution-plan.ts +42 -15
- package/src/runtime/graph-designer.ts +11 -7
- package/src/runtime/helper-model.ts +135 -48
- package/src/runtime/index.ts +7 -7
- package/src/runtime/indexed-repositories-policy.ts +3 -3
- package/src/runtime/{memory-block.ts → memory/memory-block.ts} +40 -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} +1 -1
- package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
- package/src/runtime/{memory-scope.ts → memory/memory-scope.ts} +12 -6
- package/src/runtime/plugin-resolution.ts +144 -24
- package/src/runtime/plugin-types.ts +9 -1
- package/src/runtime/post-turn-side-effects.ts +197 -130
- package/src/runtime/retrieval-adapters.ts +38 -4
- package/src/runtime/runtime-config.ts +150 -61
- package/src/runtime/runtime-extensions.ts +21 -34
- package/src/runtime/social-chat/social-chat-agent-runner.ts +157 -0
- package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +42 -20
- package/src/runtime/social-chat/social-chat.ts +594 -0
- package/src/runtime/specialist-runner.ts +36 -10
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +427 -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 +172 -94
- package/src/runtime/turn-lifecycle.ts +93 -27
- package/src/services/agent-activity.service.ts +287 -203
- package/src/services/agent-executor.service.ts +329 -217
- package/src/services/artifact.service.ts +225 -148
- package/src/services/attachment.service.ts +137 -115
- package/src/services/autonomous-job.service.ts +888 -491
- package/src/services/chat-run-registry.service.ts +11 -1
- package/src/services/context-compaction.service.ts +136 -86
- package/src/services/document-chunk.service.ts +162 -90
- 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 +256 -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 +80 -170
- package/src/services/graph-full-routing.ts +182 -0
- package/src/services/index.ts +18 -21
- package/src/services/institutional-memory.service.ts +220 -123
- package/src/services/learned-skill.service.ts +364 -259
- package/src/services/memory/memory-conversation.ts +95 -0
- package/src/services/memory/memory-org-memory.ts +39 -0
- package/src/services/memory/memory-preseeded.ts +80 -0
- package/src/services/memory/memory-rerank.ts +297 -0
- package/src/services/{memory-utils.ts → memory/memory-utils.ts} +5 -5
- package/src/services/memory/memory.service.ts +692 -0
- package/src/services/memory/rerank.service.ts +209 -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 +17 -16
- package/src/services/organization-member.service.ts +120 -66
- package/src/services/organization.service.ts +144 -51
- package/src/services/ownership-dispatcher.service.ts +415 -264
- 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/plan-approval.service.ts +102 -0
- 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 +175 -0
- package/src/services/plan/plan-coordination.service.ts +181 -0
- package/src/services/plan/plan-cycle.service.ts +398 -0
- package/src/services/plan/plan-deadline.service.ts +547 -0
- package/src/services/plan/plan-event-delivery.service.ts +261 -0
- package/src/services/plan/plan-executor-context.ts +35 -0
- package/src/services/plan/plan-executor-graph.ts +475 -0
- package/src/services/plan/plan-executor-helpers.ts +322 -0
- package/src/services/plan/plan-executor-persistence.ts +209 -0
- package/src/services/plan/plan-executor.service.ts +1654 -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 +644 -0
- package/src/services/plan/plan-scheduler.service.ts +385 -0
- package/src/services/plan/plan-template.service.ts +224 -0
- package/src/services/plan/plan-transaction-events.ts +33 -0
- package/src/services/plan/plan-validator.service.ts +907 -0
- package/src/services/plan/plan-workspace.service.ts +125 -0
- package/src/services/plugin-executor.service.ts +97 -68
- package/src/services/quality-metrics.service.ts +112 -94
- package/src/services/queue-job.service.ts +296 -230
- package/src/services/recent-activity-title.service.ts +65 -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 +176 -125
- package/src/services/system-executor.service.ts +91 -61
- package/src/services/thread/thread-active-run.ts +203 -0
- package/src/services/thread/thread-bootstrap.ts +369 -0
- package/src/services/thread/thread-listing.ts +198 -0
- package/src/services/thread/thread-memory-block.ts +117 -0
- package/src/services/thread/thread-message.service.ts +363 -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 +1146 -0
- package/src/services/thread/thread-turn-streaming.ts +402 -0
- package/src/services/thread/thread-turn-tracing.ts +35 -0
- package/src/services/thread/thread-turn.ts +343 -0
- package/src/services/thread/thread.service.ts +335 -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 +331 -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 +2 -2
- package/src/system-agents/delegated-agent-factory.ts +159 -90
- package/src/system-agents/memory-reranker.agent.ts +2 -2
- package/src/system-agents/memory.agent.ts +2 -2
- package/src/system-agents/recent-activity-title-refiner.agent.ts +2 -2
- package/src/system-agents/regular-chat-memory-digest.agent.ts +2 -2
- package/src/system-agents/skill-extractor.agent.ts +2 -2
- package/src/system-agents/skill-manager.agent.ts +2 -2
- package/src/system-agents/thread-router.agent.ts +157 -113
- package/src/system-agents/title-generator.agent.ts +2 -2
- package/src/tools/execution-plan.tool.ts +220 -161
- package/src/tools/fetch-webpage.tool.ts +21 -17
- package/src/tools/firecrawl-client.ts +16 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/memory-block.tool.ts +14 -6
- package/src/tools/plan-approval.tool.ts +49 -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 +26 -22
- package/src/tools/search.tool.ts +41 -29
- package/src/tools/team-think.tool.ts +124 -83
- package/src/tools/user-questions.tool.ts +4 -3
- package/src/tools/web-tool-shared.ts +6 -0
- package/src/utils/async.ts +17 -23
- package/src/utils/crypto.ts +21 -0
- package/src/utils/date-time.ts +40 -1
- package/src/utils/errors.ts +95 -16
- 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 +186 -51
- 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 +175 -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 +56 -31
- package/src/config/debug-logger.ts +0 -47
- package/src/redis/connection-accessor.ts +0 -26
- package/src/runtime/context-compaction-runtime.ts +0 -87
- 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-approval.service.ts +0 -83
- 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
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
import { Context, Schema, Effect, Layer } from 'effect'
|
|
2
|
+
|
|
3
|
+
import { aiLogger } from '../../config/logger'
|
|
4
|
+
import type { Memory } from '../../db/memory'
|
|
5
|
+
import { isUniqueIndexConflict } from '../../db/memory-store.helpers'
|
|
6
|
+
import type {
|
|
7
|
+
AddOptions,
|
|
8
|
+
ExtractedFact,
|
|
9
|
+
MemoryListScalar,
|
|
10
|
+
MemoryRecord,
|
|
11
|
+
MemorySearchResult,
|
|
12
|
+
MemoryType,
|
|
13
|
+
RelationType,
|
|
14
|
+
} from '../../db/memory-types'
|
|
15
|
+
import { DatabaseServiceTag, RuntimeConfigServiceTag } from '../../effect/services'
|
|
16
|
+
import { withOrgMemoryLockEffect } from '../../redis/org-memory-lock'
|
|
17
|
+
import type { HelperModelRuntime } from '../../runtime/helper-model'
|
|
18
|
+
import { HelperModelTag } from '../../runtime/helper-model'
|
|
19
|
+
import { ORG_SCOPE_PREFIX, agentScopeId, scopeId } from '../../runtime/memory/memory-scope'
|
|
20
|
+
import {
|
|
21
|
+
countScopedRetrievalCandidates,
|
|
22
|
+
executeScopedRetrieval,
|
|
23
|
+
scopedRetrievalToMap,
|
|
24
|
+
} from '../../runtime/retrieval-adapters'
|
|
25
|
+
import type { ResolvedLotaRuntimeConfig } from '../../runtime/runtime-config'
|
|
26
|
+
import { clampImportance, compactWhitespace } from '../../utils/string'
|
|
27
|
+
import type { MemoryRerankOutput } from './memory-rerank'
|
|
28
|
+
import { formatMemoryResults, formatRerankedResults, getCandidateLimit } from './memory-utils'
|
|
29
|
+
export type { MemoryRerankOutput } from './memory-rerank'
|
|
30
|
+
import {
|
|
31
|
+
buildConversationMessages,
|
|
32
|
+
isRoutableAgentName,
|
|
33
|
+
resolveAgentScopeNames,
|
|
34
|
+
shouldSkipExtractedFacts,
|
|
35
|
+
} from './memory-conversation'
|
|
36
|
+
import { getOrgMemory, makeOrgMemoryCache } from './memory-org-memory'
|
|
37
|
+
import type { OrgMemoryCache } from './memory-org-memory'
|
|
38
|
+
import { getTopMemoriesSection } from './memory-preseeded'
|
|
39
|
+
import { rerankCandidates, rerankCandidatesMultiScope } from './memory-rerank'
|
|
40
|
+
import type { makeRerankService } from './rerank.service'
|
|
41
|
+
import { RerankServiceTag } from './rerank.service'
|
|
42
|
+
|
|
43
|
+
const ORG_MEMORY_TYPE = 'fact'
|
|
44
|
+
const MAX_MEMORY_RESULTS_PER_SCOPE = 10
|
|
45
|
+
const ONBOARDING_MEMORY_MAX_FACTS = 16
|
|
46
|
+
const ONBOARDING_MEMORY_EXTRACTION_PROMPT =
|
|
47
|
+
'Onboarding mode is active. Extract multiple concrete startup facts from user-provided context: company mission, product capabilities, customer segments, pricing, traction, go-to-market plans, roadmap, team composition, technical stack, risks, and referenced URLs. Prefer one fact per concrete claim.'
|
|
48
|
+
const DIRECT_MEMORY_ASSESSMENT_PROMPT =
|
|
49
|
+
'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.'
|
|
50
|
+
|
|
51
|
+
class MemoryServiceError extends Schema.TaggedErrorClass<MemoryServiceError>()('MemoryServiceError', {
|
|
52
|
+
message: Schema.String,
|
|
53
|
+
cause: Schema.Defect,
|
|
54
|
+
}) {}
|
|
55
|
+
|
|
56
|
+
class InvalidAgentNameError extends Schema.TaggedErrorClass<InvalidAgentNameError>()('InvalidAgentNameError', {
|
|
57
|
+
agentName: Schema.String,
|
|
58
|
+
}) {}
|
|
59
|
+
|
|
60
|
+
function memoryTryPromise<A>(
|
|
61
|
+
message: string,
|
|
62
|
+
thunk: () => PromiseLike<A> | Effect.Effect<A, unknown>,
|
|
63
|
+
): Effect.Effect<A, MemoryServiceError> {
|
|
64
|
+
return Effect.suspend(() => {
|
|
65
|
+
try {
|
|
66
|
+
const value = thunk()
|
|
67
|
+
if (Effect.isEffect(value)) {
|
|
68
|
+
return value.pipe(Effect.mapError((cause) => new MemoryServiceError({ message, cause })))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return Effect.tryPromise({
|
|
72
|
+
try: () => Promise.resolve(value),
|
|
73
|
+
catch: (cause) => new MemoryServiceError({ message, cause }),
|
|
74
|
+
})
|
|
75
|
+
} catch (cause) {
|
|
76
|
+
return Effect.fail(new MemoryServiceError({ message, cause }))
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
type EffectSuccess<T> = T extends Effect.Effect<infer A, infer _E, infer _R> ? A : Awaited<T>
|
|
82
|
+
|
|
83
|
+
function searchMemoriesEffect({
|
|
84
|
+
runtimeConfig,
|
|
85
|
+
helperModelRuntime,
|
|
86
|
+
resolveReranker,
|
|
87
|
+
query,
|
|
88
|
+
memory,
|
|
89
|
+
scopeId,
|
|
90
|
+
memoryType,
|
|
91
|
+
fastMode = true,
|
|
92
|
+
}: {
|
|
93
|
+
runtimeConfig: ResolvedLotaRuntimeConfig
|
|
94
|
+
helperModelRuntime: HelperModelRuntime
|
|
95
|
+
resolveReranker: () => ReturnType<typeof makeRerankService>
|
|
96
|
+
query: string
|
|
97
|
+
memory: Memory
|
|
98
|
+
scopeId: string
|
|
99
|
+
memoryType: MemoryType
|
|
100
|
+
fastMode?: boolean
|
|
101
|
+
}): Effect.Effect<string, MemoryServiceError> {
|
|
102
|
+
return Effect.gen(function* () {
|
|
103
|
+
const limit = runtimeConfig.memory.searchK
|
|
104
|
+
const candidateLimit = fastMode ? limit : getCandidateLimit(limit)
|
|
105
|
+
const candidates = yield* memoryTryPromise('Failed to search memory candidates.', () =>
|
|
106
|
+
memory.searchCandidates(query, {
|
|
107
|
+
scopeId,
|
|
108
|
+
limit: candidateLimit,
|
|
109
|
+
memoryType,
|
|
110
|
+
fastMode,
|
|
111
|
+
includeNeighborContext: !fastMode,
|
|
112
|
+
}),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
aiLogger.debug`Memory search candidates (scopeId: ${scopeId}, candidates: ${candidates.length})`
|
|
116
|
+
|
|
117
|
+
if (candidates.length === 0) {
|
|
118
|
+
return 'No stored memories.'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (fastMode || candidates.length <= limit) {
|
|
122
|
+
aiLogger.debug`Skipping reranking (candidates: ${candidates.length} <= limit: ${limit})`
|
|
123
|
+
return formatMemoryResults(candidates.slice(0, limit))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const reranked = yield* rerankCandidates(
|
|
127
|
+
runtimeConfig,
|
|
128
|
+
helperModelRuntime,
|
|
129
|
+
query,
|
|
130
|
+
candidates,
|
|
131
|
+
limit,
|
|
132
|
+
resolveReranker(),
|
|
133
|
+
)
|
|
134
|
+
return formatRerankedResults(reranked, candidates, limit)
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function applyPreparedScopeUpdatesEffect(params: {
|
|
139
|
+
orgId: string
|
|
140
|
+
orgMemory: Memory
|
|
141
|
+
preparedUpdates: EffectSuccess<ReturnType<Memory['prepareFactsToScopes']>>
|
|
142
|
+
acquireLock?: boolean
|
|
143
|
+
}) {
|
|
144
|
+
return Effect.gen(function* () {
|
|
145
|
+
if (params.preparedUpdates.length === 0) {
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (params.acquireLock === false) {
|
|
150
|
+
yield* memoryTryPromise('Failed to apply prepared memory scope updates.', () =>
|
|
151
|
+
params.orgMemory.applyPreparedScopeUpdates(params.preparedUpdates),
|
|
152
|
+
)
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
yield* withOrgMemoryLockEffect(params.orgId, () =>
|
|
157
|
+
memoryTryPromise('Failed to apply prepared memory scope updates.', () =>
|
|
158
|
+
params.orgMemory.applyPreparedScopeUpdates(params.preparedUpdates),
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function formatBatchedResults(
|
|
165
|
+
reranked: MemoryRerankOutput | null,
|
|
166
|
+
candidatesByScopeTag: Map<string, MemorySearchResult[]>,
|
|
167
|
+
limit: number,
|
|
168
|
+
agentName?: string,
|
|
169
|
+
): string {
|
|
170
|
+
if (reranked && reranked.sections.length > 0) {
|
|
171
|
+
const allCandidates = Array.from(candidatesByScopeTag.values()).flat()
|
|
172
|
+
return formatRerankedResults(reranked, allCandidates, limit)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const sections: string[] = []
|
|
176
|
+
|
|
177
|
+
if (agentName) {
|
|
178
|
+
const agentCandidates = candidatesByScopeTag.get(`agent:${agentName}`) ?? []
|
|
179
|
+
if (agentCandidates.length > 0) {
|
|
180
|
+
sections.push(`Agent memory (${agentName}):\n${formatMemoryResults(agentCandidates.slice(0, limit))}`)
|
|
181
|
+
} else {
|
|
182
|
+
sections.push(`Agent memory (${agentName}):\nNo stored memories.`)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const orgCandidates = candidatesByScopeTag.get('org') ?? []
|
|
187
|
+
if (orgCandidates.length > 0) {
|
|
188
|
+
sections.push(
|
|
189
|
+
`${agentName ? 'Global org memory' : 'Organization memory'}:\n${formatMemoryResults(orgCandidates.slice(0, limit))}`,
|
|
190
|
+
)
|
|
191
|
+
} else {
|
|
192
|
+
sections.push(`${agentName ? 'Global org memory' : 'Organization memory'}:\nNo stored memories.`)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return sections.join('\n\n')
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface MemoryServiceDeps {
|
|
199
|
+
runtimeConfig: ResolvedLotaRuntimeConfig
|
|
200
|
+
rerankService: ReturnType<typeof makeRerankService>
|
|
201
|
+
helperModelRuntime: HelperModelRuntime
|
|
202
|
+
orgMemoryCache: OrgMemoryCache
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function createMemoryService(deps: MemoryServiceDeps) {
|
|
206
|
+
const { runtimeConfig, helperModelRuntime, orgMemoryCache } = deps
|
|
207
|
+
const resolveRerankService = () => deps.rerankService
|
|
208
|
+
const service = {
|
|
209
|
+
searchOrganizationMemories: Effect.fn('MemoryService.searchOrganizationMemories')(function* (
|
|
210
|
+
orgId: string,
|
|
211
|
+
query: string,
|
|
212
|
+
) {
|
|
213
|
+
const orgScopeId = scopeId(ORG_SCOPE_PREFIX, orgId)
|
|
214
|
+
aiLogger.debug`Organization memory search requested (orgId: ${orgId}, scopeId: ${orgScopeId}, queryLength: ${query.length})`
|
|
215
|
+
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
216
|
+
const results = yield* searchMemoriesEffect({
|
|
217
|
+
runtimeConfig,
|
|
218
|
+
helperModelRuntime,
|
|
219
|
+
resolveReranker: resolveRerankService,
|
|
220
|
+
query,
|
|
221
|
+
memory,
|
|
222
|
+
scopeId: orgScopeId,
|
|
223
|
+
memoryType: ORG_MEMORY_TYPE,
|
|
224
|
+
})
|
|
225
|
+
aiLogger.debug`Organization memory search completed (resultLength: ${results.length}, preview: ${results.slice(0, 100)})`
|
|
226
|
+
return results
|
|
227
|
+
}),
|
|
228
|
+
|
|
229
|
+
getStaleMemories(orgId: string) {
|
|
230
|
+
return Effect.gen(function* () {
|
|
231
|
+
const orgScopeId = scopeId(ORG_SCOPE_PREFIX, orgId)
|
|
232
|
+
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
233
|
+
return yield* memoryTryPromise('Failed to load stale memories.', () => memory.getStaleMemories(orgScopeId))
|
|
234
|
+
}).pipe(
|
|
235
|
+
Effect.withSpan('MemoryService.getStaleMemories'),
|
|
236
|
+
Effect.map((stale) => {
|
|
237
|
+
if (stale.length === 0) return ''
|
|
238
|
+
const items = stale.map((m) => `- [NEEDS REVIEW] ${m.content}`).join('\n')
|
|
239
|
+
return `Memories flagged for review (parent fact was superseded):\n${items}`
|
|
240
|
+
}),
|
|
241
|
+
Effect.catch((error) => {
|
|
242
|
+
aiLogger.error`Failed to get stale memories: ${error.cause}`
|
|
243
|
+
return Effect.succeed('')
|
|
244
|
+
}),
|
|
245
|
+
)
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
searchOrganizationMemoriesRaw: Effect.fn('MemoryService.searchOrganizationMemoriesRaw')(function* (
|
|
249
|
+
orgId: string,
|
|
250
|
+
query: string,
|
|
251
|
+
options?: { fastMode?: boolean; limit?: number },
|
|
252
|
+
) {
|
|
253
|
+
const orgScopeId = scopeId(ORG_SCOPE_PREFIX, orgId)
|
|
254
|
+
aiLogger.debug`searchOrganizationMemoriesRaw - orgId: "${orgId}", scopeId: "${orgScopeId}"`
|
|
255
|
+
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
256
|
+
const fastMode = options?.fastMode ?? true
|
|
257
|
+
const searchK = runtimeConfig.memory.searchK
|
|
258
|
+
const limit = options?.limit ?? (fastMode ? Math.min(searchK, 4) : searchK)
|
|
259
|
+
const candidates = yield* memoryTryPromise('Failed to search organization memories.', () =>
|
|
260
|
+
memory.searchCandidates(query, {
|
|
261
|
+
scopeId: orgScopeId,
|
|
262
|
+
limit,
|
|
263
|
+
memoryType: ORG_MEMORY_TYPE,
|
|
264
|
+
fastMode,
|
|
265
|
+
includeNeighborContext: !fastMode,
|
|
266
|
+
}),
|
|
267
|
+
)
|
|
268
|
+
aiLogger.debug`Organization memory search (raw) completed (candidates: ${candidates.length})`
|
|
269
|
+
return formatMemoryResults(candidates)
|
|
270
|
+
}),
|
|
271
|
+
|
|
272
|
+
searchAgentMemories: Effect.fn('MemoryService.searchAgentMemories')(function* (
|
|
273
|
+
orgId: string,
|
|
274
|
+
agentName: string,
|
|
275
|
+
query: string,
|
|
276
|
+
) {
|
|
277
|
+
if (!isRoutableAgentName(agentName)) {
|
|
278
|
+
aiLogger.debug`Agent memory search skipped - invalid agentName: ${agentName}`
|
|
279
|
+
return 'No stored memories.'
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const scoped = agentScopeId(orgId, agentName)
|
|
283
|
+
aiLogger.debug`Agent memory search requested (orgId: ${orgId}, agentName: ${agentName}, scopeId: ${scoped}, queryLength: ${query.length})`
|
|
284
|
+
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
285
|
+
const results = yield* searchMemoriesEffect({
|
|
286
|
+
runtimeConfig,
|
|
287
|
+
helperModelRuntime,
|
|
288
|
+
resolveReranker: resolveRerankService,
|
|
289
|
+
query,
|
|
290
|
+
memory,
|
|
291
|
+
scopeId: scoped,
|
|
292
|
+
memoryType: ORG_MEMORY_TYPE,
|
|
293
|
+
})
|
|
294
|
+
aiLogger.debug`Agent memory search completed (agentName: ${agentName}, resultLength: ${results.length}, preview: ${results.slice(0, 100)})`
|
|
295
|
+
return results
|
|
296
|
+
}),
|
|
297
|
+
|
|
298
|
+
listOrganizationMemoryRecords: Effect.fn('MemoryService.listOrganizationMemoryRecords')(function* (params: {
|
|
299
|
+
orgId: string
|
|
300
|
+
limit?: number
|
|
301
|
+
memoryType?: MemoryType
|
|
302
|
+
metadataEquals?: Record<string, MemoryListScalar>
|
|
303
|
+
metadataNotEquals?: Record<string, MemoryListScalar>
|
|
304
|
+
sort?: 'createdAtAsc' | 'createdAtDesc'
|
|
305
|
+
}) {
|
|
306
|
+
const { orgId, ...listOptions } = params
|
|
307
|
+
const orgMemory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
308
|
+
return yield* memoryTryPromise('Failed to list organization memory records.', () =>
|
|
309
|
+
orgMemory.list({ scopeId: scopeId(ORG_SCOPE_PREFIX, orgId), ...listOptions }),
|
|
310
|
+
)
|
|
311
|
+
}),
|
|
312
|
+
|
|
313
|
+
getTopMemories(params: { orgId: string; agentName?: string; limit?: number }) {
|
|
314
|
+
return getTopMemoriesSection({
|
|
315
|
+
orgMemoryCache,
|
|
316
|
+
orgId: params.orgId,
|
|
317
|
+
agentName: params.agentName,
|
|
318
|
+
limit: params.limit,
|
|
319
|
+
})
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
searchAllMemoriesBatched: Effect.fn('MemoryService.searchAllMemoriesBatched')(function* ({
|
|
323
|
+
orgId,
|
|
324
|
+
agentName,
|
|
325
|
+
query,
|
|
326
|
+
fastMode = true,
|
|
327
|
+
allowMultiScopeRerank = true,
|
|
328
|
+
}: {
|
|
329
|
+
orgId: string
|
|
330
|
+
agentName?: string
|
|
331
|
+
query: string
|
|
332
|
+
fastMode?: boolean
|
|
333
|
+
allowMultiScopeRerank?: boolean
|
|
334
|
+
}) {
|
|
335
|
+
const limit = Math.min(runtimeConfig.memory.searchK, MAX_MEMORY_RESULTS_PER_SCOPE)
|
|
336
|
+
const candidateLimit = fastMode ? limit : getCandidateLimit(limit)
|
|
337
|
+
const orgScopeId = scopeId(ORG_SCOPE_PREFIX, orgId)
|
|
338
|
+
const orgMemory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
339
|
+
|
|
340
|
+
const retrievalTasks = [
|
|
341
|
+
{
|
|
342
|
+
scopeTag: 'org',
|
|
343
|
+
retrieve: () =>
|
|
344
|
+
orgMemory.searchCandidates(query, {
|
|
345
|
+
scopeId: orgScopeId,
|
|
346
|
+
limit: candidateLimit,
|
|
347
|
+
memoryType: ORG_MEMORY_TYPE,
|
|
348
|
+
fastMode,
|
|
349
|
+
includeNeighborContext: !fastMode,
|
|
350
|
+
}),
|
|
351
|
+
},
|
|
352
|
+
]
|
|
353
|
+
|
|
354
|
+
if (isRoutableAgentName(agentName)) {
|
|
355
|
+
const agentScoped = agentScopeId(orgId, agentName)
|
|
356
|
+
retrievalTasks.push({
|
|
357
|
+
scopeTag: `agent:${agentName}`,
|
|
358
|
+
retrieve: () =>
|
|
359
|
+
orgMemory.searchCandidates(query, {
|
|
360
|
+
scopeId: agentScoped,
|
|
361
|
+
limit: candidateLimit,
|
|
362
|
+
memoryType: ORG_MEMORY_TYPE,
|
|
363
|
+
fastMode,
|
|
364
|
+
includeNeighborContext: !fastMode,
|
|
365
|
+
}),
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const results = yield* memoryTryPromise('Failed to execute scoped memory retrieval.', () =>
|
|
370
|
+
executeScopedRetrieval<MemorySearchResult>(retrievalTasks),
|
|
371
|
+
)
|
|
372
|
+
const totalCandidates = countScopedRetrievalCandidates(results)
|
|
373
|
+
aiLogger.debug`Batched memory search candidates (scopes: ${results.length}, total: ${totalCandidates})`
|
|
374
|
+
|
|
375
|
+
if (totalCandidates === 0) {
|
|
376
|
+
return 'No stored memories.'
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const candidatesByScopeTag = scopedRetrievalToMap(results)
|
|
380
|
+
if (fastMode || !allowMultiScopeRerank) {
|
|
381
|
+
return formatBatchedResults(null, candidatesByScopeTag, limit, agentName)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const reranked = yield* rerankCandidatesMultiScope(
|
|
385
|
+
runtimeConfig,
|
|
386
|
+
helperModelRuntime,
|
|
387
|
+
query,
|
|
388
|
+
results,
|
|
389
|
+
limit,
|
|
390
|
+
resolveRerankService(),
|
|
391
|
+
)
|
|
392
|
+
return formatBatchedResults(reranked, candidatesByScopeTag, limit, agentName)
|
|
393
|
+
}),
|
|
394
|
+
|
|
395
|
+
createOrganizationMemory({
|
|
396
|
+
orgId,
|
|
397
|
+
content,
|
|
398
|
+
memoryType,
|
|
399
|
+
metadata,
|
|
400
|
+
importance,
|
|
401
|
+
durability,
|
|
402
|
+
}: {
|
|
403
|
+
orgId: string
|
|
404
|
+
content: string
|
|
405
|
+
memoryType: MemoryType
|
|
406
|
+
metadata?: Record<string, unknown>
|
|
407
|
+
importance?: number
|
|
408
|
+
durability?: MemoryRecord['durability']
|
|
409
|
+
}) {
|
|
410
|
+
return Effect.gen(function* () {
|
|
411
|
+
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
412
|
+
return yield* memoryTryPromise('Failed to create organization memory.', () =>
|
|
413
|
+
memory.insert(content, {
|
|
414
|
+
scopeId: scopeId(ORG_SCOPE_PREFIX, orgId),
|
|
415
|
+
memoryType,
|
|
416
|
+
importance: importance ?? 1,
|
|
417
|
+
durability,
|
|
418
|
+
metadata: { orgId, ...metadata },
|
|
419
|
+
}),
|
|
420
|
+
)
|
|
421
|
+
}).pipe(
|
|
422
|
+
Effect.withSpan('MemoryService.createOrganizationMemory'),
|
|
423
|
+
Effect.tap(() =>
|
|
424
|
+
Effect.sync(() => {
|
|
425
|
+
const orgScopeId = scopeId(ORG_SCOPE_PREFIX, orgId)
|
|
426
|
+
aiLogger.debug`createOrganizationMemory - orgId: "${orgId}", scopeId: "${orgScopeId}", content preview: "${content.slice(0, 50)}"`
|
|
427
|
+
}),
|
|
428
|
+
),
|
|
429
|
+
Effect.catch((error) => {
|
|
430
|
+
if (isUniqueIndexConflict(error, 'memoryHashIdx')) {
|
|
431
|
+
aiLogger.debug`Organization memory already exists (hash conflict)`
|
|
432
|
+
return Effect.succeed('')
|
|
433
|
+
}
|
|
434
|
+
return Effect.fail(error)
|
|
435
|
+
}),
|
|
436
|
+
)
|
|
437
|
+
},
|
|
438
|
+
|
|
439
|
+
addOrganizationMemoryRelation: Effect.fn('MemoryService.addOrganizationMemoryRelation')(function* ({
|
|
440
|
+
orgId,
|
|
441
|
+
fromMemoryId,
|
|
442
|
+
toMemoryId,
|
|
443
|
+
relationType,
|
|
444
|
+
confidence,
|
|
445
|
+
}: {
|
|
446
|
+
orgId: string
|
|
447
|
+
fromMemoryId: string
|
|
448
|
+
toMemoryId: string
|
|
449
|
+
relationType: RelationType
|
|
450
|
+
confidence?: number
|
|
451
|
+
}) {
|
|
452
|
+
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
453
|
+
return yield* memoryTryPromise('Failed to add organization memory relation.', () =>
|
|
454
|
+
memory.addRelation(fromMemoryId, toMemoryId, relationType, confidence),
|
|
455
|
+
)
|
|
456
|
+
}),
|
|
457
|
+
|
|
458
|
+
createAgentMemory({
|
|
459
|
+
orgId,
|
|
460
|
+
agentName,
|
|
461
|
+
content,
|
|
462
|
+
memoryType,
|
|
463
|
+
metadata,
|
|
464
|
+
importance,
|
|
465
|
+
}: {
|
|
466
|
+
orgId: string
|
|
467
|
+
agentName: string
|
|
468
|
+
content: string
|
|
469
|
+
memoryType: MemoryType
|
|
470
|
+
metadata?: Record<string, unknown>
|
|
471
|
+
importance?: number
|
|
472
|
+
}) {
|
|
473
|
+
if (!isRoutableAgentName(agentName)) {
|
|
474
|
+
return Effect.fail(new InvalidAgentNameError({ agentName }))
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const scoped = agentScopeId(orgId, agentName)
|
|
478
|
+
return Effect.gen(function* () {
|
|
479
|
+
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
480
|
+
return yield* memoryTryPromise('Failed to create agent memory.', () =>
|
|
481
|
+
memory.insert(content, {
|
|
482
|
+
scopeId: scoped,
|
|
483
|
+
memoryType,
|
|
484
|
+
importance: importance ?? 1,
|
|
485
|
+
metadata: { orgId, agentName, memoryScope: 'agent', ...metadata },
|
|
486
|
+
}),
|
|
487
|
+
)
|
|
488
|
+
}).pipe(
|
|
489
|
+
Effect.withSpan('MemoryService.createAgentMemory'),
|
|
490
|
+
Effect.catch((error) => {
|
|
491
|
+
if (isUniqueIndexConflict(error, 'memoryHashIdx')) {
|
|
492
|
+
aiLogger.debug`Agent memory already exists (hash conflict)`
|
|
493
|
+
return Effect.succeed('')
|
|
494
|
+
}
|
|
495
|
+
return Effect.fail(error)
|
|
496
|
+
}),
|
|
497
|
+
)
|
|
498
|
+
},
|
|
499
|
+
|
|
500
|
+
assessMemoryCandidate: Effect.fn('MemoryService.assessMemoryCandidate')(function* (params: {
|
|
501
|
+
orgId: string
|
|
502
|
+
content: string
|
|
503
|
+
}) {
|
|
504
|
+
const trimmed = compactWhitespace(params.content)
|
|
505
|
+
if (!trimmed) return null
|
|
506
|
+
|
|
507
|
+
const memory = yield* getOrgMemory(orgMemoryCache, params.orgId)
|
|
508
|
+
const facts = yield* memoryTryPromise('Failed to assess direct memory candidate.', () =>
|
|
509
|
+
memory.extractFactsFromMessages([{ role: 'user', content: trimmed }], {
|
|
510
|
+
maxFacts: 1,
|
|
511
|
+
customPrompt: DIRECT_MEMORY_ASSESSMENT_PROMPT,
|
|
512
|
+
}),
|
|
513
|
+
)
|
|
514
|
+
if (facts.length === 0) return null
|
|
515
|
+
const fact = facts[0]
|
|
516
|
+
|
|
517
|
+
return {
|
|
518
|
+
classification: fact.classification,
|
|
519
|
+
durability: fact.durability,
|
|
520
|
+
importance: clampImportance(fact.importance),
|
|
521
|
+
rationale: fact.rationale,
|
|
522
|
+
}
|
|
523
|
+
}),
|
|
524
|
+
|
|
525
|
+
updateOrganizationMemoryById: Effect.fn('MemoryService.updateOrganizationMemoryById')(function* ({
|
|
526
|
+
orgId,
|
|
527
|
+
memoryId,
|
|
528
|
+
content,
|
|
529
|
+
}: {
|
|
530
|
+
orgId: string
|
|
531
|
+
memoryId: string
|
|
532
|
+
content: string
|
|
533
|
+
}) {
|
|
534
|
+
const memory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
535
|
+
return yield* memoryTryPromise('Failed to update organization memory.', () =>
|
|
536
|
+
memory.updateMemory(memoryId, content),
|
|
537
|
+
)
|
|
538
|
+
}),
|
|
539
|
+
|
|
540
|
+
addExtractedFactsToScopes: Effect.fn('MemoryService.addExtractedFactsToScopes')(function* (params: {
|
|
541
|
+
orgId: string
|
|
542
|
+
facts: ExtractedFact[]
|
|
543
|
+
source: string
|
|
544
|
+
sourceMetadata?: Record<string, unknown>
|
|
545
|
+
agentNames?: string[]
|
|
546
|
+
acquireLock?: boolean
|
|
547
|
+
}) {
|
|
548
|
+
if (params.facts.length === 0) return
|
|
549
|
+
|
|
550
|
+
const orgMemory = yield* getOrgMemory(orgMemoryCache, params.orgId)
|
|
551
|
+
const scopes: AddOptions[] = [
|
|
552
|
+
{
|
|
553
|
+
scopeId: scopeId(ORG_SCOPE_PREFIX, params.orgId),
|
|
554
|
+
memoryType: ORG_MEMORY_TYPE,
|
|
555
|
+
metadata: { orgId: params.orgId, source: params.source, ...params.sourceMetadata },
|
|
556
|
+
},
|
|
557
|
+
]
|
|
558
|
+
|
|
559
|
+
for (const scopedAgentName of resolveAgentScopeNames(undefined, params.agentNames ?? [])) {
|
|
560
|
+
scopes.push({
|
|
561
|
+
scopeId: agentScopeId(params.orgId, scopedAgentName),
|
|
562
|
+
memoryType: ORG_MEMORY_TYPE,
|
|
563
|
+
metadata: {
|
|
564
|
+
orgId: params.orgId,
|
|
565
|
+
source: params.source,
|
|
566
|
+
...params.sourceMetadata,
|
|
567
|
+
agentName: scopedAgentName,
|
|
568
|
+
memoryScope: 'agent',
|
|
569
|
+
},
|
|
570
|
+
})
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const preparedUpdates = yield* memoryTryPromise('Failed to prepare extracted facts for memory scopes.', () =>
|
|
574
|
+
orgMemory.prepareFactsToScopes(params.facts, scopes),
|
|
575
|
+
)
|
|
576
|
+
yield* applyPreparedScopeUpdatesEffect({
|
|
577
|
+
orgId: params.orgId,
|
|
578
|
+
orgMemory,
|
|
579
|
+
preparedUpdates,
|
|
580
|
+
acquireLock: params.acquireLock,
|
|
581
|
+
})
|
|
582
|
+
}),
|
|
583
|
+
|
|
584
|
+
addConversationMemories: Effect.fn('MemoryService.addConversationMemories')(function* ({
|
|
585
|
+
orgId,
|
|
586
|
+
input,
|
|
587
|
+
output,
|
|
588
|
+
sourceId,
|
|
589
|
+
source = 'chat',
|
|
590
|
+
sourceMetadata,
|
|
591
|
+
onboardStatus,
|
|
592
|
+
agentName,
|
|
593
|
+
historyMessages = [],
|
|
594
|
+
memoryBlock,
|
|
595
|
+
attachmentContext,
|
|
596
|
+
agentNames = [],
|
|
597
|
+
}: {
|
|
598
|
+
orgId: string
|
|
599
|
+
input: string
|
|
600
|
+
output: string
|
|
601
|
+
sourceId?: string
|
|
602
|
+
source?: string
|
|
603
|
+
sourceMetadata?: Record<string, unknown>
|
|
604
|
+
onboardStatus?: string
|
|
605
|
+
agentName?: string
|
|
606
|
+
historyMessages?: Array<{ role: 'user' | 'agent'; content: string; agentName?: string }>
|
|
607
|
+
memoryBlock?: string
|
|
608
|
+
attachmentContext?: string
|
|
609
|
+
agentNames?: string[]
|
|
610
|
+
}) {
|
|
611
|
+
const { messages, normalizedInput, sanitizedOutput } = buildConversationMessages({
|
|
612
|
+
input,
|
|
613
|
+
output,
|
|
614
|
+
historyMessages,
|
|
615
|
+
memoryBlock,
|
|
616
|
+
attachmentContext,
|
|
617
|
+
})
|
|
618
|
+
|
|
619
|
+
if (!normalizedInput || !sanitizedOutput || messages.length === 0) {
|
|
620
|
+
aiLogger.debug`Skipping memory add - empty input or output`
|
|
621
|
+
return
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const orgScopeId = scopeId(ORG_SCOPE_PREFIX, orgId)
|
|
625
|
+
aiLogger.debug`addConversationMemories - orgId: "${orgId}", scopeId: "${orgScopeId}", sourceId: ${sourceId ?? 'none'}`
|
|
626
|
+
|
|
627
|
+
const onboardingActive = onboardStatus !== undefined && onboardStatus !== 'completed'
|
|
628
|
+
const extractionConfig = onboardingActive
|
|
629
|
+
? { maxFacts: ONBOARDING_MEMORY_MAX_FACTS, customPrompt: ONBOARDING_MEMORY_EXTRACTION_PROMPT }
|
|
630
|
+
: undefined
|
|
631
|
+
const orgMemory = yield* getOrgMemory(orgMemoryCache, orgId)
|
|
632
|
+
|
|
633
|
+
const scopes: AddOptions[] = [
|
|
634
|
+
{
|
|
635
|
+
scopeId: orgScopeId,
|
|
636
|
+
memoryType: ORG_MEMORY_TYPE,
|
|
637
|
+
metadata: { orgId, source, ...(sourceId ? { sourceId } : {}), ...sourceMetadata },
|
|
638
|
+
},
|
|
639
|
+
]
|
|
640
|
+
|
|
641
|
+
for (const scopedAgentName of resolveAgentScopeNames(agentName, agentNames)) {
|
|
642
|
+
const agentId = agentScopeId(orgId, scopedAgentName)
|
|
643
|
+
scopes.push({
|
|
644
|
+
scopeId: agentId,
|
|
645
|
+
memoryType: ORG_MEMORY_TYPE,
|
|
646
|
+
metadata: {
|
|
647
|
+
orgId,
|
|
648
|
+
agentName: scopedAgentName,
|
|
649
|
+
memoryScope: 'agent',
|
|
650
|
+
source,
|
|
651
|
+
...(sourceId ? { sourceId } : {}),
|
|
652
|
+
...sourceMetadata,
|
|
653
|
+
},
|
|
654
|
+
})
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const extractedFacts = yield* memoryTryPromise('Failed to extract conversation memories.', () =>
|
|
658
|
+
orgMemory.extractFactsFromMessages(messages, extractionConfig),
|
|
659
|
+
)
|
|
660
|
+
if (shouldSkipExtractedFacts(extractedFacts)) {
|
|
661
|
+
aiLogger.debug`Skipping transient conversation memory`
|
|
662
|
+
return
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const preparedUpdates = yield* memoryTryPromise('Failed to prepare conversation memories for scopes.', () =>
|
|
666
|
+
orgMemory.prepareFactsToScopes(extractedFacts, scopes),
|
|
667
|
+
)
|
|
668
|
+
yield* applyPreparedScopeUpdatesEffect({ orgId, orgMemory, preparedUpdates })
|
|
669
|
+
if (preparedUpdates.length > 0) {
|
|
670
|
+
aiLogger.debug`Conversation memories added to ${scopes.length} scope(s) from ${messages.length} message(s)`
|
|
671
|
+
}
|
|
672
|
+
}),
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return service
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
export class MemoryServiceTag extends Context.Service<MemoryServiceTag, ReturnType<typeof createMemoryService>>()(
|
|
679
|
+
'MemoryService',
|
|
680
|
+
) {}
|
|
681
|
+
|
|
682
|
+
export const MemoryServiceLive = Layer.effect(
|
|
683
|
+
MemoryServiceTag,
|
|
684
|
+
Effect.gen(function* () {
|
|
685
|
+
const db = yield* DatabaseServiceTag
|
|
686
|
+
const runtimeConfig = yield* RuntimeConfigServiceTag
|
|
687
|
+
const rerankService = yield* RerankServiceTag
|
|
688
|
+
const helperModelRuntime = yield* HelperModelTag
|
|
689
|
+
const orgMemoryCache = yield* makeOrgMemoryCache({ db, runtimeConfig, helperModelRuntime })
|
|
690
|
+
return createMemoryService({ runtimeConfig, rerankService, helperModelRuntime, orgMemoryCache })
|
|
691
|
+
}),
|
|
692
|
+
)
|