@lota-sdk/core 0.4.8 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -12
- package/src/ai/embedding-cache.ts +96 -22
- package/src/ai-gateway/ai-gateway.ts +766 -223
- package/src/config/agent-defaults.ts +189 -75
- package/src/config/agent-types.ts +54 -4
- package/src/config/background-processing.ts +1 -1
- package/src/config/constants.ts +8 -2
- package/src/config/index.ts +0 -1
- package/src/config/logger.ts +299 -19
- package/src/config/thread-defaults.ts +40 -20
- package/src/create-runtime.ts +200 -449
- package/src/db/base.service.ts +52 -28
- package/src/db/cursor-pagination.ts +71 -30
- package/src/db/memory-query-builder.ts +2 -1
- package/src/db/memory-store.helpers.ts +4 -7
- package/src/db/memory-store.ts +868 -601
- package/src/db/memory.ts +396 -280
- package/src/db/record-id.ts +32 -10
- package/src/db/schema-fingerprint.ts +30 -12
- package/src/db/service-normalization.ts +288 -0
- package/src/db/service.ts +912 -779
- package/src/db/startup.ts +153 -68
- package/src/db/transaction-conflict.ts +15 -0
- package/src/effect/awaitable-effect.ts +96 -0
- package/src/effect/errors.ts +121 -0
- package/src/effect/helpers.ts +123 -0
- package/src/effect/index.ts +24 -0
- package/src/effect/layers.ts +238 -0
- package/src/effect/runtime-ref.ts +25 -0
- package/src/effect/runtime.ts +46 -0
- package/src/effect/services.ts +61 -0
- package/src/effect/zod.ts +43 -0
- package/src/embeddings/provider.ts +128 -83
- package/src/index.ts +48 -1
- package/src/openrouter/direct-provider.ts +11 -35
- package/src/queues/autonomous-job.queue.ts +117 -73
- package/src/queues/context-compaction.queue.ts +50 -17
- package/src/queues/delayed-node-promotion.queue.ts +46 -17
- package/src/queues/document-processor.queue.ts +52 -77
- package/src/queues/memory-consolidation.queue.ts +47 -32
- package/src/queues/organization-learning.queue.ts +26 -4
- package/src/queues/plan-agent-heartbeat.queue.ts +71 -24
- package/src/queues/plan-scheduler.queue.ts +97 -33
- package/src/queues/post-chat-memory.queue.ts +56 -26
- package/src/queues/queue-factory.ts +227 -59
- package/src/queues/standalone-worker.ts +39 -0
- package/src/queues/title-generation.queue.ts +45 -11
- package/src/redis/connection.ts +182 -113
- package/src/redis/index.ts +6 -8
- package/src/redis/org-memory-lock.ts +60 -27
- package/src/redis/redis-lease-lock.ts +200 -121
- package/src/redis/runtime-connection.ts +20 -0
- package/src/redis/stream-context.ts +92 -46
- package/src/runtime/agent-identity-overrides.ts +2 -2
- package/src/runtime/agent-runtime-policy.ts +5 -2
- package/src/runtime/agent-stream-helpers.ts +24 -9
- package/src/runtime/chat-run-orchestration.ts +102 -19
- package/src/runtime/chat-run-registry.ts +36 -2
- package/src/runtime/context-compaction/context-compaction-runtime.ts +107 -0
- package/src/runtime/{context-compaction.ts → context-compaction/context-compaction.ts} +161 -94
- package/src/runtime/domain-layer.ts +192 -0
- package/src/runtime/execution-plan-visibility.ts +2 -2
- package/src/runtime/execution-plan.ts +42 -15
- package/src/runtime/graph-designer.ts +16 -4
- package/src/runtime/helper-model.ts +139 -48
- package/src/runtime/index.ts +7 -8
- package/src/runtime/indexed-repositories-policy.ts +3 -3
- package/src/runtime/{memory-block.ts → memory/memory-block.ts} +50 -36
- package/src/runtime/{memory-digest-policy.ts → memory/memory-digest-policy.ts} +1 -1
- package/src/runtime/{memory-pipeline.ts → memory/memory-pipeline.ts} +54 -67
- package/src/runtime/{memory-prompts-fact.ts → memory/memory-prompts-fact.ts} +2 -2
- package/src/runtime/memory/memory-scope.ts +53 -0
- package/src/runtime/plugin-resolution.ts +124 -25
- package/src/runtime/plugin-types.ts +9 -1
- package/src/runtime/post-turn-side-effects.ts +177 -130
- package/src/runtime/retrieval-adapters.ts +40 -6
- package/src/runtime/runtime-accessors.ts +92 -0
- package/src/runtime/runtime-config.ts +150 -61
- package/src/runtime/runtime-extensions.ts +23 -25
- package/src/runtime/runtime-lifecycle.ts +124 -0
- package/src/runtime/runtime-services.ts +386 -0
- package/src/runtime/runtime-token.ts +47 -0
- package/src/runtime/social-chat/social-chat-agent-runner.ts +159 -0
- package/src/runtime/{social-chat-history.ts → social-chat/social-chat-history.ts} +51 -20
- package/src/runtime/social-chat/social-chat.ts +630 -0
- package/src/runtime/specialist-runner.ts +36 -10
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +433 -0
- package/src/runtime/{team-consultation-prompts.ts → team-consultation/team-consultation-prompts.ts} +6 -2
- package/src/runtime/thread-chat-helpers.ts +2 -2
- package/src/runtime/thread-plan-turn.ts +2 -1
- package/src/runtime/thread-turn-context.ts +183 -111
- package/src/runtime/turn-lifecycle.ts +93 -27
- package/src/services/agent-activity.service.ts +287 -203
- package/src/services/agent-executor.service.ts +253 -149
- package/src/services/artifact.service.ts +231 -149
- package/src/services/attachment.service.ts +171 -115
- package/src/services/autonomous-job.service.ts +890 -491
- package/src/services/background-work.service.ts +54 -0
- package/src/services/chat-run-registry.service.ts +13 -1
- package/src/services/context-compaction.service.ts +136 -86
- package/src/services/document-chunk.service.ts +151 -88
- package/src/services/execution-plan/execution-plan-approval.ts +26 -0
- package/src/services/execution-plan/execution-plan-context.ts +29 -0
- package/src/services/execution-plan/execution-plan-graph.ts +278 -0
- package/src/services/execution-plan/execution-plan-schedule.ts +84 -0
- package/src/services/execution-plan/execution-plan-spec.ts +75 -0
- package/src/services/execution-plan/execution-plan.service.ts +1041 -0
- package/src/services/feedback-loop.service.ts +132 -76
- package/src/services/global-orchestrator.service.ts +101 -168
- package/src/services/graph-full-routing.ts +193 -0
- package/src/services/index.ts +19 -21
- package/src/services/institutional-memory.service.ts +213 -125
- package/src/services/learned-skill.service.ts +368 -260
- package/src/services/memory/memory-conversation.ts +95 -0
- package/src/services/memory/memory-errors.ts +27 -0
- package/src/services/memory/memory-org-memory.ts +50 -0
- package/src/services/memory/memory-preseeded.ts +86 -0
- package/src/services/memory/memory-rerank.ts +297 -0
- package/src/services/{memory-utils.ts → memory/memory-utils.ts} +6 -5
- package/src/services/memory/memory.service.ts +674 -0
- package/src/services/memory/rerank.service.ts +201 -0
- package/src/services/monitoring-window.service.ts +92 -70
- package/src/services/mutating-approval.service.ts +62 -53
- package/src/services/node-workspace.service.ts +141 -98
- package/src/services/notification.service.ts +29 -16
- package/src/services/organization-member.service.ts +120 -66
- package/src/services/organization.service.ts +153 -77
- package/src/services/ownership-dispatcher.service.ts +456 -263
- package/src/services/plan/plan-agent-heartbeat.service.ts +234 -0
- package/src/services/plan/plan-agent-query.service.ts +322 -0
- package/src/services/{plan-approval.service.ts → plan/plan-approval.service.ts} +45 -22
- package/src/services/plan/plan-artifact.service.ts +60 -0
- package/src/services/plan/plan-builder.service.ts +76 -0
- package/src/services/plan/plan-checkpoint.service.ts +103 -0
- package/src/services/{plan-compiler.service.ts → plan/plan-compiler.service.ts} +26 -9
- package/src/services/plan/plan-completion-side-effects.ts +169 -0
- package/src/services/plan/plan-coordination.service.ts +181 -0
- package/src/services/plan/plan-cycle.service.ts +405 -0
- package/src/services/plan/plan-deadline.service.ts +533 -0
- package/src/services/plan/plan-event-delivery.service.ts +266 -0
- package/src/services/plan/plan-executor-context.ts +35 -0
- package/src/services/plan/plan-executor-graph.ts +522 -0
- package/src/services/plan/plan-executor-helpers.ts +307 -0
- package/src/services/plan/plan-executor-persistence.ts +209 -0
- package/src/services/plan/plan-executor.service.ts +1737 -0
- package/src/services/{plan-helpers.ts → plan/plan-helpers.ts} +1 -1
- package/src/services/{plan-run-data.ts → plan/plan-run-data.ts} +4 -4
- package/src/services/plan/plan-run-serialization.ts +15 -0
- package/src/services/plan/plan-run.service.ts +637 -0
- package/src/services/plan/plan-scheduler.service.ts +379 -0
- package/src/services/plan/plan-template.service.ts +224 -0
- package/src/services/plan/plan-transaction-events.ts +36 -0
- package/src/services/plan/plan-validator.service.ts +907 -0
- package/src/services/plan/plan-workspace.service.ts +131 -0
- package/src/services/plugin-executor.service.ts +102 -68
- package/src/services/quality-metrics.service.ts +112 -94
- package/src/services/queue-job.service.ts +288 -231
- package/src/services/recent-activity-title.service.ts +73 -36
- package/src/services/recent-activity.service.ts +274 -259
- package/src/services/skill-resolver.service.ts +38 -12
- package/src/services/social-chat-history.service.ts +190 -122
- package/src/services/system-executor.service.ts +96 -61
- package/src/services/thread/thread-active-run.ts +203 -0
- package/src/services/thread/thread-bootstrap.ts +385 -0
- package/src/services/thread/thread-listing.ts +199 -0
- package/src/services/thread/thread-memory-block.ts +130 -0
- package/src/services/thread/thread-message.service.ts +379 -0
- package/src/services/thread/thread-record-store.ts +155 -0
- package/src/services/thread/thread-title.service.ts +74 -0
- package/src/services/thread/thread-turn-execution.ts +280 -0
- package/src/services/thread/thread-turn-message-context.ts +73 -0
- package/src/services/thread/thread-turn-preparation.service.ts +1148 -0
- package/src/services/thread/thread-turn-streaming.ts +403 -0
- package/src/services/thread/thread-turn-tracing.ts +35 -0
- package/src/services/thread/thread-turn.ts +376 -0
- package/src/services/thread/thread.service.ts +344 -0
- package/src/services/user.service.ts +82 -32
- package/src/services/write-intent-validator.service.ts +63 -51
- package/src/storage/attachment-parser.ts +69 -27
- package/src/storage/attachment-storage.service.ts +334 -275
- package/src/storage/generated-document-storage.service.ts +66 -34
- package/src/system-agents/agent-result.ts +3 -1
- package/src/system-agents/context-compaction.agent.ts +3 -3
- package/src/system-agents/delegated-agent-factory.ts +159 -90
- package/src/system-agents/helper-agent-options.ts +1 -1
- package/src/system-agents/memory-reranker.agent.ts +3 -3
- package/src/system-agents/memory.agent.ts +3 -3
- package/src/system-agents/recent-activity-title-refiner.agent.ts +3 -3
- package/src/system-agents/regular-chat-memory-digest.agent.ts +3 -3
- package/src/system-agents/skill-extractor.agent.ts +3 -3
- package/src/system-agents/skill-manager.agent.ts +3 -3
- package/src/system-agents/thread-router.agent.ts +157 -113
- package/src/system-agents/title-generator.agent.ts +3 -3
- package/src/tools/execution-plan.tool.ts +241 -171
- package/src/tools/fetch-webpage.tool.ts +29 -18
- package/src/tools/firecrawl-client.ts +26 -6
- package/src/tools/index.ts +1 -0
- package/src/tools/memory-block.tool.ts +14 -6
- package/src/tools/plan-approval.tool.ts +57 -47
- package/src/tools/read-file-parts.tool.ts +44 -33
- package/src/tools/remember-memory.tool.ts +65 -45
- package/src/tools/search-web.tool.ts +33 -22
- package/src/tools/search.tool.ts +41 -29
- package/src/tools/team-think.tool.ts +125 -84
- package/src/tools/user-questions.tool.ts +4 -3
- package/src/tools/web-tool-shared.ts +6 -0
- package/src/utils/async.ts +25 -22
- package/src/utils/crypto.ts +21 -0
- package/src/utils/date-time.ts +40 -1
- package/src/utils/errors.ts +111 -20
- package/src/utils/hono-error-handler.ts +24 -39
- package/src/utils/index.ts +2 -1
- package/src/utils/null-proto-record.ts +41 -0
- package/src/utils/sse-keepalive.ts +124 -21
- package/src/workers/bootstrap.ts +164 -52
- package/src/workers/memory-consolidation.worker.ts +325 -237
- package/src/workers/organization-learning.worker.ts +50 -16
- package/src/workers/regular-chat-memory-digest.helpers.ts +28 -27
- package/src/workers/regular-chat-memory-digest.runner.ts +185 -114
- package/src/workers/skill-extraction.runner.ts +176 -93
- package/src/workers/utils/file-section-chunker.ts +8 -10
- package/src/workers/utils/repo-structure-extractor.ts +349 -260
- package/src/workers/utils/repomix-file-sections.ts +2 -2
- package/src/workers/utils/thread-message-query.ts +97 -38
- package/src/workers/worker-utils.ts +74 -31
- package/src/config/debug-logger.ts +0 -47
- package/src/config/search.ts +0 -3
- package/src/redis/connection-accessor.ts +0 -26
- package/src/runtime/agent-types.ts +0 -1
- package/src/runtime/context-compaction-runtime.ts +0 -87
- package/src/runtime/memory-scope.ts +0 -43
- package/src/runtime/social-chat-agent-runner.ts +0 -118
- package/src/runtime/social-chat.ts +0 -516
- package/src/runtime/team-consultation-orchestrator.ts +0 -272
- package/src/services/adaptive-playbook.service.ts +0 -152
- package/src/services/artifact-provenance.service.ts +0 -172
- package/src/services/chat-attachments.service.ts +0 -17
- package/src/services/context-compaction-runtime.singleton.ts +0 -13
- package/src/services/execution-plan.service.ts +0 -1118
- package/src/services/memory.service.ts +0 -914
- package/src/services/plan-agent-heartbeat.service.ts +0 -136
- package/src/services/plan-agent-query.service.ts +0 -267
- package/src/services/plan-artifact.service.ts +0 -50
- package/src/services/plan-builder.service.ts +0 -67
- package/src/services/plan-checkpoint.service.ts +0 -81
- package/src/services/plan-completion-side-effects.ts +0 -80
- package/src/services/plan-coordination.service.ts +0 -157
- package/src/services/plan-cycle.service.ts +0 -284
- package/src/services/plan-deadline.service.ts +0 -430
- package/src/services/plan-event-delivery.service.ts +0 -166
- package/src/services/plan-executor.service.ts +0 -1950
- package/src/services/plan-run.service.ts +0 -515
- package/src/services/plan-scheduler.service.ts +0 -240
- package/src/services/plan-template.service.ts +0 -177
- package/src/services/plan-validator.service.ts +0 -818
- package/src/services/plan-workspace.service.ts +0 -83
- package/src/services/rerank.service.ts +0 -156
- package/src/services/thread-message.service.ts +0 -275
- package/src/services/thread-plan-registry.service.ts +0 -22
- package/src/services/thread-title.service.ts +0 -39
- package/src/services/thread-turn-preparation.service.ts +0 -1147
- package/src/services/thread-turn.ts +0 -172
- package/src/services/thread.service.ts +0 -869
- package/src/utils/env.ts +0 -8
- /package/src/runtime/{context-compaction-constants.ts → context-compaction/context-compaction-constants.ts} +0 -0
- /package/src/runtime/{memory-format.ts → memory/memory-format.ts} +0 -0
- /package/src/runtime/{memory-prompts-parse.ts → memory/memory-prompts-parse.ts} +0 -0
- /package/src/runtime/{memory-prompts-update.ts → memory/memory-prompts-update.ts} +0 -0
- /package/src/runtime/{social-chat-prompts.ts → social-chat/social-chat-prompts.ts} +0 -0
- /package/src/services/{plan-node-spec.ts → plan/plan-node-spec.ts} +0 -0
- /package/src/services/{thread-constants.ts → thread/thread-constants.ts} +0 -0
- /package/src/services/{thread.types.ts → thread/thread.types.ts} +0 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { getAgentRoster } from '../../config/agent-defaults'
|
|
2
|
+
import type { ExtractedFact, Message } from '../../db/memory-types'
|
|
3
|
+
import { sanitizeAgentOutputForMemory } from '../../runtime/llm-content'
|
|
4
|
+
import { compactWhitespace, truncateText } from '../../utils/string'
|
|
5
|
+
|
|
6
|
+
const MAX_CONVERSATION_HISTORY_MESSAGES = 24
|
|
7
|
+
const MAX_CONVERSATION_MESSAGE_CHARS = 1_200
|
|
8
|
+
const MAX_CONVERSATION_MEMORY_BLOCK_CHARS = 2_000
|
|
9
|
+
const MAX_CONVERSATION_ATTACHMENT_CONTEXT_CHARS = 6_000
|
|
10
|
+
const LOW_VALUE_MEMORY_IMPORTANCE_THRESHOLD = 0.45
|
|
11
|
+
|
|
12
|
+
export const isRoutableAgentName = (value?: string): value is string =>
|
|
13
|
+
Boolean(value && getAgentRoster().includes(value))
|
|
14
|
+
|
|
15
|
+
function normalizeConversationText(value: string, maxChars: number): string {
|
|
16
|
+
const normalized = compactWhitespace(value)
|
|
17
|
+
if (!normalized) return ''
|
|
18
|
+
return truncateText(normalized, maxChars)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function resolveAgentScopeNames(agentName?: string, agentNames: string[] = []): string[] {
|
|
22
|
+
const unique = new Set<string>()
|
|
23
|
+
if (typeof agentName === 'string' && agentName.trim()) {
|
|
24
|
+
unique.add(agentName.trim())
|
|
25
|
+
}
|
|
26
|
+
for (const candidate of agentNames) {
|
|
27
|
+
if (typeof candidate !== 'string') continue
|
|
28
|
+
const normalized = candidate.trim()
|
|
29
|
+
if (!normalized) continue
|
|
30
|
+
unique.add(normalized)
|
|
31
|
+
}
|
|
32
|
+
return [...unique].filter((name) => isRoutableAgentName(name))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function buildConversationMessages(params: {
|
|
36
|
+
input: string
|
|
37
|
+
output: string
|
|
38
|
+
historyMessages: Array<{ role: 'user' | 'agent'; content: string; agentName?: string }>
|
|
39
|
+
memoryBlock?: string
|
|
40
|
+
attachmentContext?: string
|
|
41
|
+
}): { messages: Message[]; normalizedInput: string; sanitizedOutput: string } {
|
|
42
|
+
const normalizedInput = normalizeConversationText(params.input, MAX_CONVERSATION_MESSAGE_CHARS)
|
|
43
|
+
const sanitizedOutput = normalizeConversationText(
|
|
44
|
+
sanitizeAgentOutputForMemory(params.output),
|
|
45
|
+
MAX_CONVERSATION_MESSAGE_CHARS,
|
|
46
|
+
)
|
|
47
|
+
if (!normalizedInput || !sanitizedOutput) {
|
|
48
|
+
return { messages: [], normalizedInput, sanitizedOutput }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const messages: Message[] = []
|
|
52
|
+
|
|
53
|
+
const normalizedHistory: Message[] = params.historyMessages
|
|
54
|
+
.map((message): Message | null => {
|
|
55
|
+
const role: Message['role'] = message.role === 'agent' ? 'agent' : 'user'
|
|
56
|
+
const normalized = normalizeConversationText(
|
|
57
|
+
role === 'agent' ? sanitizeAgentOutputForMemory(message.content) : message.content,
|
|
58
|
+
MAX_CONVERSATION_MESSAGE_CHARS,
|
|
59
|
+
)
|
|
60
|
+
if (!normalized) return null
|
|
61
|
+
return { role, content: normalized }
|
|
62
|
+
})
|
|
63
|
+
.filter((message): message is Message => message !== null)
|
|
64
|
+
.slice(-MAX_CONVERSATION_HISTORY_MESSAGES)
|
|
65
|
+
|
|
66
|
+
messages.push(...normalizedHistory)
|
|
67
|
+
|
|
68
|
+
const normalizedMemoryBlock = normalizeConversationText(params.memoryBlock ?? '', MAX_CONVERSATION_MEMORY_BLOCK_CHARS)
|
|
69
|
+
if (normalizedMemoryBlock) {
|
|
70
|
+
messages.push({ role: 'user', content: `Thread memory block:\n${normalizedMemoryBlock}` })
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const normalizedAttachmentContext = normalizeConversationText(
|
|
74
|
+
params.attachmentContext ?? '',
|
|
75
|
+
MAX_CONVERSATION_ATTACHMENT_CONTEXT_CHARS,
|
|
76
|
+
)
|
|
77
|
+
if (normalizedAttachmentContext) {
|
|
78
|
+
messages.push({ role: 'user', content: `Attachment context:\n${normalizedAttachmentContext}` })
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
messages.push({ role: 'user', content: normalizedInput })
|
|
82
|
+
messages.push({ role: 'agent', content: sanitizedOutput })
|
|
83
|
+
|
|
84
|
+
return { messages, normalizedInput, sanitizedOutput }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function shouldSkipExtractedFacts(facts: ExtractedFact[]): boolean {
|
|
88
|
+
if (facts.length === 0) return true
|
|
89
|
+
return facts.every(
|
|
90
|
+
(fact) =>
|
|
91
|
+
fact.durability === 'ephemeral' &&
|
|
92
|
+
fact.importance <= LOW_VALUE_MEMORY_IMPORTANCE_THRESHOLD &&
|
|
93
|
+
fact.classification !== 'durable',
|
|
94
|
+
)
|
|
95
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect, Schema } from 'effect'
|
|
2
|
+
|
|
3
|
+
export class MemoryServiceError extends Schema.TaggedErrorClass<MemoryServiceError>()('MemoryServiceError', {
|
|
4
|
+
message: Schema.String,
|
|
5
|
+
cause: Schema.Defect,
|
|
6
|
+
}) {}
|
|
7
|
+
|
|
8
|
+
export function tryMemoryPromise<A, E, R = never>(
|
|
9
|
+
message: string,
|
|
10
|
+
thunk: () => PromiseLike<A> | Effect.Effect<A, E, R>,
|
|
11
|
+
): Effect.Effect<A, MemoryServiceError, R> {
|
|
12
|
+
return Effect.suspend(() => {
|
|
13
|
+
try {
|
|
14
|
+
const value = thunk()
|
|
15
|
+
if (Effect.isEffect(value)) {
|
|
16
|
+
return value.pipe(Effect.mapError((cause) => new MemoryServiceError({ message, cause })))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return Effect.tryPromise({
|
|
20
|
+
try: () => Promise.resolve(value),
|
|
21
|
+
catch: (cause) => new MemoryServiceError({ message, cause }),
|
|
22
|
+
})
|
|
23
|
+
} catch (cause) {
|
|
24
|
+
return Effect.fail(new MemoryServiceError({ message, cause }))
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Context } from 'effect'
|
|
2
|
+
import { Cache, Duration, Effect } from 'effect'
|
|
3
|
+
|
|
4
|
+
import { aiLogger } from '../../config/logger'
|
|
5
|
+
import { Memory } from '../../db/memory'
|
|
6
|
+
import type { SurrealDBService } from '../../db/service'
|
|
7
|
+
import type { HelperModelRuntime } from '../../runtime/helper-model'
|
|
8
|
+
import { ORG_SCOPE_PREFIX, scopeId } from '../../runtime/memory/memory-scope'
|
|
9
|
+
import type { ResolvedLotaRuntimeConfig } from '../../runtime/runtime-config'
|
|
10
|
+
import { createOrgMemoryAgent, ORG_MEMORY_PROMPT } from '../../system-agents/memory.agent'
|
|
11
|
+
import type { BackgroundWorkService } from '../background-work.service'
|
|
12
|
+
|
|
13
|
+
const MAX_ORG_MEMORY_CLIENTS = 128
|
|
14
|
+
|
|
15
|
+
interface OrgMemoryDeps {
|
|
16
|
+
db: SurrealDBService
|
|
17
|
+
runtimeConfig: ResolvedLotaRuntimeConfig
|
|
18
|
+
helperModelRuntime: HelperModelRuntime
|
|
19
|
+
background: Context.Service.Shape<typeof BackgroundWorkService>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type OrgMemoryCache = Cache.Cache<string, Memory>
|
|
23
|
+
|
|
24
|
+
export function makeOrgMemoryCache(deps: OrgMemoryDeps) {
|
|
25
|
+
return Cache.make({
|
|
26
|
+
lookup: (cacheKey: string) =>
|
|
27
|
+
Effect.sync(() => {
|
|
28
|
+
aiLogger.debug`Memory client created and cached for ${cacheKey}`
|
|
29
|
+
return new Memory(
|
|
30
|
+
{
|
|
31
|
+
db: deps.db,
|
|
32
|
+
runtimeConfig: deps.runtimeConfig,
|
|
33
|
+
helperModelRuntime: deps.helperModelRuntime,
|
|
34
|
+
background: deps.background,
|
|
35
|
+
},
|
|
36
|
+
{ createAgent: createOrgMemoryAgent },
|
|
37
|
+
{ customPrompt: ORG_MEMORY_PROMPT },
|
|
38
|
+
)
|
|
39
|
+
}),
|
|
40
|
+
capacity: MAX_ORG_MEMORY_CLIENTS,
|
|
41
|
+
timeToLive: Duration.infinity,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function getOrgMemory(cache: OrgMemoryCache, orgId: string) {
|
|
46
|
+
return Effect.gen(function* () {
|
|
47
|
+
const key = yield* scopeId(ORG_SCOPE_PREFIX, orgId)
|
|
48
|
+
return yield* Cache.get(cache, key)
|
|
49
|
+
})
|
|
50
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Effect } from 'effect'
|
|
2
|
+
|
|
3
|
+
import type { Memory } from '../../db/memory'
|
|
4
|
+
import type { MemoryRecord } from '../../db/memory-types'
|
|
5
|
+
import type { MemoryScopeError } from '../../runtime/memory/memory-scope'
|
|
6
|
+
import { ORG_SCOPE_PREFIX, agentScopeId, scopeId } from '../../runtime/memory/memory-scope'
|
|
7
|
+
import { compactWhitespace, truncateText } from '../../utils/string'
|
|
8
|
+
import type { OrgMemoryCache } from './memory-org-memory'
|
|
9
|
+
import { getOrgMemory } from './memory-org-memory'
|
|
10
|
+
|
|
11
|
+
const PRESEEDED_MEMORY_LIMIT = 5
|
|
12
|
+
const PRESEEDED_MEMORY_MAX_CHARS = 300
|
|
13
|
+
const PRESEEDED_MIN_IMPORTANCE = 0.7
|
|
14
|
+
const PRESEEDED_MEMORY_DURABILITY: MemoryRecord['durability'] = 'core'
|
|
15
|
+
type PreSeededMemoryError = Effect.Error<ReturnType<Memory['listTopMemories']>> | MemoryScopeError
|
|
16
|
+
|
|
17
|
+
function normalizePreSeededMemoryText(value: string): string {
|
|
18
|
+
return truncateText(compactWhitespace(value), PRESEEDED_MEMORY_MAX_CHARS)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function formatPreSeededMemoriesSection(memories: MemoryRecord[]): string | undefined {
|
|
22
|
+
const lines = memories
|
|
23
|
+
.map((memory) => normalizePreSeededMemoryText(memory.content))
|
|
24
|
+
.filter((line) => line.length > 0)
|
|
25
|
+
.map((line) => `- ${line}`)
|
|
26
|
+
if (lines.length === 0) return undefined
|
|
27
|
+
|
|
28
|
+
return ['<pre-seeded-memories>', ...lines, '</pre-seeded-memories>'].join('\n')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getTopMemoriesSection(params: {
|
|
32
|
+
orgMemoryCache: OrgMemoryCache
|
|
33
|
+
orgId: string
|
|
34
|
+
agentName?: string
|
|
35
|
+
limit?: number
|
|
36
|
+
}): Effect.Effect<string | undefined, PreSeededMemoryError, never> {
|
|
37
|
+
return Effect.gen(function* () {
|
|
38
|
+
const orgMemory = yield* getOrgMemory(params.orgMemoryCache, params.orgId)
|
|
39
|
+
const orgScopeId = yield* scopeId(ORG_SCOPE_PREFIX, params.orgId)
|
|
40
|
+
const requestedLimit = params.limit ?? PRESEEDED_MEMORY_LIMIT
|
|
41
|
+
const limit = Math.max(1, Math.min(requestedLimit, PRESEEDED_MEMORY_LIMIT))
|
|
42
|
+
|
|
43
|
+
const agentScopedId =
|
|
44
|
+
params.agentName && params.agentName.trim() ? yield* agentScopeId(params.orgId, params.agentName) : undefined
|
|
45
|
+
|
|
46
|
+
const [orgTopMemories, agentTopMemories] = yield* Effect.all([
|
|
47
|
+
orgMemory.listTopMemories({
|
|
48
|
+
scopeId: orgScopeId,
|
|
49
|
+
limit,
|
|
50
|
+
memoryType: 'fact',
|
|
51
|
+
durability: PRESEEDED_MEMORY_DURABILITY,
|
|
52
|
+
minImportance: PRESEEDED_MIN_IMPORTANCE,
|
|
53
|
+
}),
|
|
54
|
+
agentScopedId
|
|
55
|
+
? orgMemory.listTopMemories({
|
|
56
|
+
scopeId: agentScopedId,
|
|
57
|
+
limit,
|
|
58
|
+
memoryType: 'fact',
|
|
59
|
+
durability: PRESEEDED_MEMORY_DURABILITY,
|
|
60
|
+
minImportance: PRESEEDED_MIN_IMPORTANCE,
|
|
61
|
+
})
|
|
62
|
+
: Effect.succeed([] as MemoryRecord[]),
|
|
63
|
+
])
|
|
64
|
+
|
|
65
|
+
const combined = [...agentTopMemories, ...orgTopMemories].sort((left, right) => {
|
|
66
|
+
if (right.importance !== left.importance) return right.importance - left.importance
|
|
67
|
+
if (right.accessCount !== left.accessCount) return right.accessCount - left.accessCount
|
|
68
|
+
const rightLastAccess = right.lastAccessedAt?.getTime() ?? 0
|
|
69
|
+
const leftLastAccess = left.lastAccessedAt?.getTime() ?? 0
|
|
70
|
+
if (rightLastAccess !== leftLastAccess) return rightLastAccess - leftLastAccess
|
|
71
|
+
return right.createdAt.getTime() - left.createdAt.getTime()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const deduped: MemoryRecord[] = []
|
|
75
|
+
const seen = new Set<string>()
|
|
76
|
+
for (const memory of combined) {
|
|
77
|
+
const normalizedKey = compactWhitespace(memory.content).toLowerCase()
|
|
78
|
+
if (!normalizedKey || seen.has(normalizedKey)) continue
|
|
79
|
+
seen.add(normalizedKey)
|
|
80
|
+
deduped.push(memory)
|
|
81
|
+
if (deduped.length >= limit) break
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return formatPreSeededMemoriesSection(deduped)
|
|
85
|
+
})
|
|
86
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { Effect, Schema } from 'effect'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
import { aiLogger } from '../../config/logger'
|
|
5
|
+
import type { MemorySearchResult } from '../../db/memory-types'
|
|
6
|
+
import type { HelperModelRuntime } from '../../runtime/helper-model'
|
|
7
|
+
import type { MemoryRerankerStrategy, ResolvedLotaRuntimeConfig } from '../../runtime/runtime-config'
|
|
8
|
+
import { createMemoryRerankerAgent, MEMORY_RERANKER_PROMPT } from '../../system-agents/memory-reranker.agent'
|
|
9
|
+
import { compactWhitespace, truncateText } from '../../utils/string'
|
|
10
|
+
import type { makeRerankService } from './rerank.service'
|
|
11
|
+
|
|
12
|
+
const RERANK_CANDIDATE_MAX_CHARS = 500
|
|
13
|
+
const RERANK_SECTION_TITLE = 'Most relevant memories'
|
|
14
|
+
|
|
15
|
+
const RERANK_PROMPT_CANDIDATE_SCHEMA = Schema.Struct({ id: Schema.String, text: Schema.String, score: Schema.Number })
|
|
16
|
+
|
|
17
|
+
const RERANK_PROMPT_MULTI_SCOPE_CANDIDATE_SCHEMA = Schema.Struct({
|
|
18
|
+
id: Schema.String,
|
|
19
|
+
text: Schema.String,
|
|
20
|
+
score: Schema.Number,
|
|
21
|
+
scope: Schema.String,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const RERANK_PROMPT_SCHEMA = Schema.Struct({
|
|
25
|
+
query: Schema.String,
|
|
26
|
+
maxItems: Schema.Number,
|
|
27
|
+
candidates: Schema.Array(RERANK_PROMPT_CANDIDATE_SCHEMA),
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const RERANK_MULTI_SCOPE_PROMPT_SCHEMA = Schema.Struct({
|
|
31
|
+
query: Schema.String,
|
|
32
|
+
maxItems: Schema.Number,
|
|
33
|
+
candidates: Schema.Array(RERANK_PROMPT_MULTI_SCOPE_CANDIDATE_SCHEMA),
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const stringifyRerankPrompt = Schema.encodeSync(Schema.fromJsonString(RERANK_PROMPT_SCHEMA))
|
|
37
|
+
const stringifyRerankMultiScopePrompt = Schema.encodeSync(Schema.fromJsonString(RERANK_MULTI_SCOPE_PROMPT_SCHEMA))
|
|
38
|
+
|
|
39
|
+
class MemoryRerankError extends Schema.TaggedErrorClass<MemoryRerankError>()('MemoryRerankError', {
|
|
40
|
+
scope: Schema.Literals(['single', 'multi-scope']),
|
|
41
|
+
message: Schema.String,
|
|
42
|
+
cause: Schema.optional(Schema.Unknown),
|
|
43
|
+
}) {}
|
|
44
|
+
|
|
45
|
+
const MemoryRerankOutputSchema = z.object({
|
|
46
|
+
sections: z.array(
|
|
47
|
+
z.object({
|
|
48
|
+
title: z.string(),
|
|
49
|
+
items: z.array(
|
|
50
|
+
z.object({
|
|
51
|
+
id: z.string(),
|
|
52
|
+
relevance: z.string().describe('Short relevance reason. Use empty string when no reason is needed.'),
|
|
53
|
+
}),
|
|
54
|
+
),
|
|
55
|
+
}),
|
|
56
|
+
),
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
export type MemoryRerankOutput = z.infer<typeof MemoryRerankOutputSchema>
|
|
60
|
+
|
|
61
|
+
function truncateCandidateText(value: string): string {
|
|
62
|
+
return truncateText(compactWhitespace(value), RERANK_CANDIDATE_MAX_CHARS)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function readNumericMetadata(metadata: Record<string, unknown>, key: string): number {
|
|
66
|
+
const value = metadata[key]
|
|
67
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value
|
|
68
|
+
return 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function buildRerankerCandidateText(candidate: MemorySearchResult): string {
|
|
72
|
+
const metadata = candidate.metadata
|
|
73
|
+
const relationCount = readNumericMetadata(metadata, 'relationCount')
|
|
74
|
+
const supportCount = readNumericMetadata(metadata, 'supportCount')
|
|
75
|
+
const contradictCount = readNumericMetadata(metadata, 'contradictCount')
|
|
76
|
+
|
|
77
|
+
const relatedContextRaw = metadata.relatedContext
|
|
78
|
+
const relatedContext = Array.isArray(relatedContextRaw)
|
|
79
|
+
? relatedContextRaw
|
|
80
|
+
.slice(0, 3)
|
|
81
|
+
.map((item) => String(item).trim())
|
|
82
|
+
.filter((item) => item.length > 0)
|
|
83
|
+
: []
|
|
84
|
+
|
|
85
|
+
const contextLines: string[] = []
|
|
86
|
+
if (relatedContext.length > 0) {
|
|
87
|
+
contextLines.push(`Related context: ${relatedContext.join(' | ')}`)
|
|
88
|
+
}
|
|
89
|
+
if (relationCount > 0 || supportCount > 0 || contradictCount > 0) {
|
|
90
|
+
contextLines.push(
|
|
91
|
+
`Graph signals: relations=${relationCount}; supports=${supportCount}; contradicts=${contradictCount}`,
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (contextLines.length === 0) return candidate.content
|
|
96
|
+
return `${candidate.content}\n\n${contextLines.join('\n')}`
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getRerankerStrategy(runtimeConfig: ResolvedLotaRuntimeConfig): MemoryRerankerStrategy {
|
|
100
|
+
return runtimeConfig.memory.rerankerStrategy
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function buildRerankOutput(ids: string[], title = RERANK_SECTION_TITLE): MemoryRerankOutput {
|
|
104
|
+
return { sections: [{ title, items: ids.map((id) => ({ id, relevance: '' })) }] }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function toRerankError(scope: 'single' | 'multi-scope', message: string, cause?: unknown): MemoryRerankError {
|
|
108
|
+
return new MemoryRerankError({ scope, message, cause })
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function logRerankError(scope: 'single' | 'multi-scope', error: unknown): void {
|
|
112
|
+
if (scope === 'multi-scope') {
|
|
113
|
+
aiLogger.error`Multi-scope memory reranker failed: ${error}`
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
aiLogger.error`Memory reranker failed: ${error}`
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function rerankCandidatesWithHelperEffect(
|
|
121
|
+
helperModelRuntime: HelperModelRuntime,
|
|
122
|
+
query: string,
|
|
123
|
+
candidates: MemorySearchResult[],
|
|
124
|
+
maxItems: number,
|
|
125
|
+
): Effect.Effect<MemoryRerankOutput, MemoryRerankError> {
|
|
126
|
+
return Effect.tryPromise({
|
|
127
|
+
try: () =>
|
|
128
|
+
helperModelRuntime.generateHelperStructured({
|
|
129
|
+
tag: 'memory-reranker',
|
|
130
|
+
createAgent: createMemoryRerankerAgent,
|
|
131
|
+
defaultSystemPrompt: MEMORY_RERANKER_PROMPT,
|
|
132
|
+
messages: [
|
|
133
|
+
{
|
|
134
|
+
role: 'user',
|
|
135
|
+
content: stringifyRerankPrompt({
|
|
136
|
+
query,
|
|
137
|
+
maxItems,
|
|
138
|
+
candidates: candidates.map((candidate) => ({
|
|
139
|
+
id: candidate.id,
|
|
140
|
+
text: truncateCandidateText(buildRerankerCandidateText(candidate)),
|
|
141
|
+
score: candidate.score,
|
|
142
|
+
})),
|
|
143
|
+
}),
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
schema: MemoryRerankOutputSchema,
|
|
147
|
+
}),
|
|
148
|
+
catch: (cause) => {
|
|
149
|
+
logRerankError('single', cause)
|
|
150
|
+
return toRerankError('single', 'Memory reranker failed.', cause)
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function rerankCandidatesWithRerankServiceEffect(
|
|
156
|
+
query: string,
|
|
157
|
+
candidates: MemorySearchResult[],
|
|
158
|
+
maxItems: number,
|
|
159
|
+
reranker: ReturnType<typeof makeRerankService>,
|
|
160
|
+
): Effect.Effect<MemoryRerankOutput, MemoryRerankError> {
|
|
161
|
+
return reranker
|
|
162
|
+
.rerankDocuments({
|
|
163
|
+
query,
|
|
164
|
+
topN: maxItems,
|
|
165
|
+
documents: candidates.map((candidate) => ({
|
|
166
|
+
id: candidate.id,
|
|
167
|
+
text: truncateCandidateText(buildRerankerCandidateText(candidate)),
|
|
168
|
+
})),
|
|
169
|
+
})
|
|
170
|
+
.pipe(
|
|
171
|
+
Effect.mapError((cause) => {
|
|
172
|
+
logRerankError('single', cause)
|
|
173
|
+
return toRerankError('single', 'Memory reranker failed.', cause)
|
|
174
|
+
}),
|
|
175
|
+
Effect.map((reranked) => buildRerankOutput(reranked.results.map((item: { id: string }) => item.id))),
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function rerankCandidates(
|
|
180
|
+
runtimeConfig: ResolvedLotaRuntimeConfig,
|
|
181
|
+
helperModelRuntime: HelperModelRuntime,
|
|
182
|
+
query: string,
|
|
183
|
+
candidates: MemorySearchResult[],
|
|
184
|
+
maxItems: number,
|
|
185
|
+
reranker?: ReturnType<typeof makeRerankService>,
|
|
186
|
+
): Effect.Effect<MemoryRerankOutput | null> {
|
|
187
|
+
if (candidates.length === 0) return Effect.succeed(null)
|
|
188
|
+
|
|
189
|
+
return Effect.gen(function* () {
|
|
190
|
+
if (getRerankerStrategy(runtimeConfig) === 'rerank' && reranker) {
|
|
191
|
+
return yield* rerankCandidatesWithRerankServiceEffect(query, candidates, maxItems, reranker)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return yield* rerankCandidatesWithHelperEffect(helperModelRuntime, query, candidates, maxItems)
|
|
195
|
+
}).pipe(
|
|
196
|
+
Effect.catchCause((cause) =>
|
|
197
|
+
Effect.sync(() => {
|
|
198
|
+
logRerankError('single', cause)
|
|
199
|
+
return null
|
|
200
|
+
}),
|
|
201
|
+
),
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function rerankCandidatesMultiScopeWithHelperEffect(
|
|
206
|
+
helperModelRuntime: HelperModelRuntime,
|
|
207
|
+
query: string,
|
|
208
|
+
flattenedCandidates: Array<MemorySearchResult & { scopeTag: string }>,
|
|
209
|
+
maxItems: number,
|
|
210
|
+
): Effect.Effect<MemoryRerankOutput, MemoryRerankError> {
|
|
211
|
+
return Effect.tryPromise({
|
|
212
|
+
try: () =>
|
|
213
|
+
helperModelRuntime.generateHelperStructured({
|
|
214
|
+
tag: 'memory-reranker-multi-scope',
|
|
215
|
+
createAgent: createMemoryRerankerAgent,
|
|
216
|
+
defaultSystemPrompt: MEMORY_RERANKER_PROMPT,
|
|
217
|
+
messages: [
|
|
218
|
+
{
|
|
219
|
+
role: 'user',
|
|
220
|
+
content: stringifyRerankMultiScopePrompt({
|
|
221
|
+
query,
|
|
222
|
+
maxItems,
|
|
223
|
+
candidates: flattenedCandidates.map((candidate) => ({
|
|
224
|
+
id: candidate.id,
|
|
225
|
+
text: truncateCandidateText(buildRerankerCandidateText(candidate)),
|
|
226
|
+
score: candidate.score,
|
|
227
|
+
scope: candidate.scopeTag,
|
|
228
|
+
})),
|
|
229
|
+
}),
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
schema: MemoryRerankOutputSchema,
|
|
233
|
+
}),
|
|
234
|
+
catch: (cause) => {
|
|
235
|
+
logRerankError('multi-scope', cause)
|
|
236
|
+
return toRerankError('multi-scope', 'Multi-scope memory reranker failed.', cause)
|
|
237
|
+
},
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function rerankCandidatesMultiScopeWithRerankServiceEffect(
|
|
242
|
+
query: string,
|
|
243
|
+
flattenedCandidates: Array<MemorySearchResult & { scopeTag: string }>,
|
|
244
|
+
maxItems: number,
|
|
245
|
+
reranker: ReturnType<typeof makeRerankService>,
|
|
246
|
+
): Effect.Effect<MemoryRerankOutput, MemoryRerankError> {
|
|
247
|
+
return reranker
|
|
248
|
+
.rerankDocuments({
|
|
249
|
+
query,
|
|
250
|
+
topN: maxItems,
|
|
251
|
+
documents: flattenedCandidates.map((candidate) => ({
|
|
252
|
+
id: candidate.id,
|
|
253
|
+
text: truncateCandidateText(`${buildRerankerCandidateText(candidate)}\n\nScope: ${candidate.scopeTag}`),
|
|
254
|
+
})),
|
|
255
|
+
})
|
|
256
|
+
.pipe(
|
|
257
|
+
Effect.mapError((cause) => {
|
|
258
|
+
logRerankError('multi-scope', cause)
|
|
259
|
+
return toRerankError('multi-scope', 'Multi-scope memory reranker failed.', cause)
|
|
260
|
+
}),
|
|
261
|
+
Effect.map((reranked) =>
|
|
262
|
+
buildRerankOutput(
|
|
263
|
+
reranked.results.map((item: { id: string }) => item.id),
|
|
264
|
+
'Top matches across memory scopes',
|
|
265
|
+
),
|
|
266
|
+
),
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function rerankCandidatesMultiScope(
|
|
271
|
+
runtimeConfig: ResolvedLotaRuntimeConfig,
|
|
272
|
+
helperModelRuntime: HelperModelRuntime,
|
|
273
|
+
query: string,
|
|
274
|
+
scopedCandidates: Array<{ scopeTag: string; candidates: MemorySearchResult[] }>,
|
|
275
|
+
maxItems: number,
|
|
276
|
+
reranker?: ReturnType<typeof makeRerankService>,
|
|
277
|
+
): Effect.Effect<MemoryRerankOutput | null> {
|
|
278
|
+
const flattened = scopedCandidates.flatMap(({ scopeTag, candidates }) =>
|
|
279
|
+
candidates.map((candidate) => ({ ...candidate, scopeTag })),
|
|
280
|
+
)
|
|
281
|
+
if (flattened.length === 0 || flattened.length <= maxItems) return Effect.succeed(null)
|
|
282
|
+
|
|
283
|
+
return Effect.gen(function* () {
|
|
284
|
+
if (getRerankerStrategy(runtimeConfig) === 'rerank' && reranker) {
|
|
285
|
+
return yield* rerankCandidatesMultiScopeWithRerankServiceEffect(query, flattened, maxItems, reranker)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return yield* rerankCandidatesMultiScopeWithHelperEffect(helperModelRuntime, query, flattened, maxItems)
|
|
289
|
+
}).pipe(
|
|
290
|
+
Effect.catchCause((cause) =>
|
|
291
|
+
Effect.sync(() => {
|
|
292
|
+
logRerankError('multi-scope', cause)
|
|
293
|
+
return null
|
|
294
|
+
}),
|
|
295
|
+
),
|
|
296
|
+
)
|
|
297
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
1
|
+
import { VECTOR_SEARCH_OVERFETCH_MULTIPLIER } from '@lota-sdk/shared'
|
|
2
|
+
|
|
3
|
+
import { MEMORY } from '../../config/constants'
|
|
4
|
+
import type { MemorySearchResult } from '../../db/memory-types'
|
|
5
|
+
import { compactWhitespace, truncateText } from '../../utils/string'
|
|
6
|
+
import type { MemoryRerankOutput } from './memory-rerank'
|
|
6
7
|
|
|
7
8
|
export function getCandidateLimit(limit: number): number {
|
|
8
9
|
return Math.max(limit * VECTOR_SEARCH_OVERFETCH_MULTIPLIER, MEMORY.DEFAULT_CANDIDATE_LIMIT)
|