@lota-sdk/core 0.4.7 → 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/model-constants.ts +1 -0
- package/src/config/thread-defaults.ts +33 -21
- package/src/create-runtime.ts +725 -383
- 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 -71
- package/src/index.ts +46 -1
- package/src/openrouter/direct-provider.ts +29 -0
- 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 +165 -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 -20
- 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 -844
- 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/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,228 @@
|
|
|
1
|
+
import type { ToolSet } from 'ai'
|
|
2
|
+
import { Effect, Layer, Logger, References } from 'effect'
|
|
3
|
+
|
|
4
|
+
import type { CoreThreadProfile } from '../config/agent-defaults'
|
|
5
|
+
import { resolveAgentConfig, resolveAgentFactoryConfig } from '../config/agent-defaults'
|
|
6
|
+
import type { AgentFactory, AgentRuntimeConfigProvider, AgentToolBuilder } from '../config/agent-types'
|
|
7
|
+
import { configureLotaLogger, getLotaLoggers, toEffectLogLevel } from '../config/logger'
|
|
8
|
+
import type { LotaLogLevel } from '../config/logger'
|
|
9
|
+
import { resolveThreadConfig } from '../config/thread-defaults'
|
|
10
|
+
import type { LotaThreadConfig } from '../config/thread-defaults'
|
|
11
|
+
import { LOTA_SDK_DATABASE_NAME } from '../db/sdk-database'
|
|
12
|
+
import type { SurrealDatabaseConfig } from '../db/service'
|
|
13
|
+
import { SurrealDBService } from '../db/service'
|
|
14
|
+
import type { CreateRedisConnectionManagerOptions } from '../redis/connection'
|
|
15
|
+
import { createRedisConnectionManager } from '../redis/connection'
|
|
16
|
+
import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
|
|
17
|
+
import type { LotaRuntimeAdapters, LotaRuntimeTurnHooks } from '../runtime/runtime-extensions'
|
|
18
|
+
import type { LotaRuntimeWorkerExtensions } from '../runtime/runtime-worker-registry'
|
|
19
|
+
import { QueueJobServiceLive } from '../services/queue-job.service'
|
|
20
|
+
import { getErrorMessage } from '../utils/errors'
|
|
21
|
+
import { DatabaseError, RedisError } from './errors'
|
|
22
|
+
import { effectTryServicePromise } from './helpers'
|
|
23
|
+
import {
|
|
24
|
+
AppLoggerTag,
|
|
25
|
+
AgentConfigServiceTag,
|
|
26
|
+
AgentFactoryServiceTag,
|
|
27
|
+
DatabaseServiceTag,
|
|
28
|
+
RedisServiceTag,
|
|
29
|
+
RuntimeAdaptersServiceTag,
|
|
30
|
+
RuntimeConfigServiceTag,
|
|
31
|
+
RuntimeWorkerExtensionsServiceTag,
|
|
32
|
+
ThreadConfigServiceTag,
|
|
33
|
+
ToolProvidersServiceTag,
|
|
34
|
+
TurnHooksServiceTag,
|
|
35
|
+
} from './services'
|
|
36
|
+
|
|
37
|
+
const EMPTY_TOOLS = {} as ToolSet
|
|
38
|
+
const EMPTY_WORKERS: LotaRuntimeWorkerExtensions = {}
|
|
39
|
+
|
|
40
|
+
export function DatabaseLive(config: SurrealDatabaseConfig) {
|
|
41
|
+
return Layer.effect(
|
|
42
|
+
DatabaseServiceTag,
|
|
43
|
+
Effect.acquireRelease(
|
|
44
|
+
Effect.try({
|
|
45
|
+
try: () => new SurrealDBService(config),
|
|
46
|
+
catch: (error) =>
|
|
47
|
+
new DatabaseError({ message: `Failed to create database service: ${getErrorMessage(error)}`, cause: error }),
|
|
48
|
+
}),
|
|
49
|
+
(databaseService) =>
|
|
50
|
+
Effect.ignore(
|
|
51
|
+
databaseService
|
|
52
|
+
.disconnect()
|
|
53
|
+
.pipe(
|
|
54
|
+
Effect.tapError((error) =>
|
|
55
|
+
Effect.logWarning(`Failed to clean up database service: ${getErrorMessage(error)}`),
|
|
56
|
+
),
|
|
57
|
+
),
|
|
58
|
+
),
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function RedisLive(config: CreateRedisConnectionManagerOptions) {
|
|
64
|
+
return Layer.effect(
|
|
65
|
+
RedisServiceTag,
|
|
66
|
+
Effect.acquireRelease(
|
|
67
|
+
Effect.try({
|
|
68
|
+
try: () => createRedisConnectionManager(config),
|
|
69
|
+
catch: (error) =>
|
|
70
|
+
new RedisError({
|
|
71
|
+
message: `Failed to create Redis connection manager: ${getErrorMessage(error)}`,
|
|
72
|
+
cause: error,
|
|
73
|
+
}),
|
|
74
|
+
}),
|
|
75
|
+
(redisManager) =>
|
|
76
|
+
Effect.ignore(
|
|
77
|
+
effectTryServicePromise(
|
|
78
|
+
() => redisManager.closeConnection(),
|
|
79
|
+
'Failed to close Redis connection manager',
|
|
80
|
+
).pipe(
|
|
81
|
+
Effect.tapError((error) =>
|
|
82
|
+
Effect.logWarning(`Failed to clean up Redis connection manager: ${getErrorMessage(error)}`),
|
|
83
|
+
),
|
|
84
|
+
),
|
|
85
|
+
),
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function AppLoggerLive(level: LotaLogLevel = 'info') {
|
|
91
|
+
return Layer.mergeAll(
|
|
92
|
+
Layer.effectDiscard(Effect.sync(() => configureLotaLogger(level))),
|
|
93
|
+
Layer.succeed(AppLoggerTag, getLotaLoggers()),
|
|
94
|
+
Logger.layer([Logger.consolePretty()]),
|
|
95
|
+
Layer.succeed(References.MinimumLogLevel, toEffectLogLevel(level)),
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function RuntimeConfigLive(config: ResolvedLotaRuntimeConfig) {
|
|
100
|
+
return Layer.succeed(RuntimeConfigServiceTag, config)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function AgentConfigLive(config: {
|
|
104
|
+
roster: readonly string[]
|
|
105
|
+
leadAgentId: string
|
|
106
|
+
displayNames: Record<string, string>
|
|
107
|
+
shortDisplayNames?: Record<string, string>
|
|
108
|
+
descriptions?: Record<string, string>
|
|
109
|
+
routerModelId?: string
|
|
110
|
+
teamConsultParticipants: readonly string[]
|
|
111
|
+
getCoreThreadProfile?: (coreType: string) => CoreThreadProfile
|
|
112
|
+
}) {
|
|
113
|
+
return Layer.succeed(AgentConfigServiceTag, resolveAgentConfig(config))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function AgentFactoryLive(config: {
|
|
117
|
+
createAgent?: AgentFactory
|
|
118
|
+
buildAgentTools?: AgentToolBuilder
|
|
119
|
+
getAgentRuntimeConfig?: AgentRuntimeConfigProvider
|
|
120
|
+
pluginRuntime?: Record<string, unknown>
|
|
121
|
+
}) {
|
|
122
|
+
return Layer.succeed(AgentFactoryServiceTag, resolveAgentFactoryConfig(config))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function ThreadConfigLive(params: { agentRoster: readonly string[]; config?: LotaThreadConfig }) {
|
|
126
|
+
return Layer.succeed(ThreadConfigServiceTag, resolveThreadConfig(params))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function RuntimeExtensionsLive(params: {
|
|
130
|
+
adapters?: LotaRuntimeAdapters
|
|
131
|
+
turnHooks?: LotaRuntimeTurnHooks
|
|
132
|
+
toolProviders?: ToolSet
|
|
133
|
+
extraWorkers?: LotaRuntimeWorkerExtensions
|
|
134
|
+
}) {
|
|
135
|
+
return Layer.mergeAll(
|
|
136
|
+
Layer.succeed(RuntimeAdaptersServiceTag, params.adapters ?? {}),
|
|
137
|
+
Layer.succeed(TurnHooksServiceTag, params.turnHooks ?? {}),
|
|
138
|
+
Layer.succeed(ToolProvidersServiceTag, params.toolProviders ?? EMPTY_TOOLS),
|
|
139
|
+
Layer.succeed(RuntimeWorkerExtensionsServiceTag, params.extraWorkers ?? EMPTY_WORKERS),
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ── Shared Infrastructure Layer Builders ─────────────────────────────
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Builds the full infrastructure layer used by `createLotaRuntime`.
|
|
147
|
+
* Contains all config, database, redis, agent, thread, and extension layers.
|
|
148
|
+
*/
|
|
149
|
+
export function buildInfrastructureLayer(
|
|
150
|
+
runtimeConfig: ResolvedLotaRuntimeConfig,
|
|
151
|
+
options: { resolvedAgentDisplayNames: Record<string, string>; observabilityLayer: Layer.Layer<never> },
|
|
152
|
+
) {
|
|
153
|
+
return Layer.mergeAll(
|
|
154
|
+
AppLoggerLive(runtimeConfig.logging.level),
|
|
155
|
+
RuntimeConfigLive(runtimeConfig),
|
|
156
|
+
AgentConfigLive({
|
|
157
|
+
roster: runtimeConfig.agents.roster,
|
|
158
|
+
leadAgentId: runtimeConfig.agents.leadAgentId,
|
|
159
|
+
displayNames: options.resolvedAgentDisplayNames,
|
|
160
|
+
shortDisplayNames: runtimeConfig.agents.shortDisplayNames,
|
|
161
|
+
descriptions: runtimeConfig.agents.descriptions,
|
|
162
|
+
routerModelId: runtimeConfig.agents.routerModelId,
|
|
163
|
+
teamConsultParticipants: runtimeConfig.agents.teamConsultParticipants,
|
|
164
|
+
getCoreThreadProfile: runtimeConfig.agents.getCoreThreadProfile,
|
|
165
|
+
}),
|
|
166
|
+
AgentFactoryLive({
|
|
167
|
+
createAgent: runtimeConfig.agents.createAgent,
|
|
168
|
+
buildAgentTools: runtimeConfig.agents.buildAgentTools,
|
|
169
|
+
getAgentRuntimeConfig: runtimeConfig.agents.getAgentRuntimeConfig,
|
|
170
|
+
pluginRuntime: runtimeConfig.pluginRuntime,
|
|
171
|
+
}),
|
|
172
|
+
ThreadConfigLive({ agentRoster: runtimeConfig.agents.roster, config: runtimeConfig.threads }),
|
|
173
|
+
RuntimeExtensionsLive({
|
|
174
|
+
adapters: runtimeConfig.runtimeAdapters,
|
|
175
|
+
turnHooks: runtimeConfig.turnHooks,
|
|
176
|
+
toolProviders: runtimeConfig.toolProviders ?? {},
|
|
177
|
+
extraWorkers: runtimeConfig.extraWorkers,
|
|
178
|
+
}),
|
|
179
|
+
DatabaseLive({
|
|
180
|
+
url: runtimeConfig.database.url,
|
|
181
|
+
namespace: runtimeConfig.database.namespace,
|
|
182
|
+
database: LOTA_SDK_DATABASE_NAME,
|
|
183
|
+
username: runtimeConfig.database.username,
|
|
184
|
+
password: runtimeConfig.database.password,
|
|
185
|
+
}),
|
|
186
|
+
RedisLive({ url: runtimeConfig.redis.url }),
|
|
187
|
+
options.observabilityLayer,
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Builds the minimal infrastructure layer used by sandboxed workers.
|
|
193
|
+
* Omits observability and uses empty runtime extensions.
|
|
194
|
+
*/
|
|
195
|
+
export function buildWorkerInfrastructureLayer(runtimeConfig: ResolvedLotaRuntimeConfig) {
|
|
196
|
+
const infrastructureLayer = Layer.mergeAll(
|
|
197
|
+
AppLoggerLive(runtimeConfig.logging.level),
|
|
198
|
+
RuntimeConfigLive(runtimeConfig),
|
|
199
|
+
AgentConfigLive({
|
|
200
|
+
roster: runtimeConfig.agents.roster,
|
|
201
|
+
leadAgentId: runtimeConfig.agents.leadAgentId,
|
|
202
|
+
displayNames: runtimeConfig.agents.displayNames,
|
|
203
|
+
shortDisplayNames: runtimeConfig.agents.shortDisplayNames,
|
|
204
|
+
descriptions: runtimeConfig.agents.descriptions,
|
|
205
|
+
routerModelId: runtimeConfig.agents.routerModelId,
|
|
206
|
+
teamConsultParticipants: runtimeConfig.agents.teamConsultParticipants,
|
|
207
|
+
getCoreThreadProfile: runtimeConfig.agents.getCoreThreadProfile,
|
|
208
|
+
}),
|
|
209
|
+
AgentFactoryLive({
|
|
210
|
+
createAgent: runtimeConfig.agents.createAgent,
|
|
211
|
+
buildAgentTools: runtimeConfig.agents.buildAgentTools,
|
|
212
|
+
getAgentRuntimeConfig: runtimeConfig.agents.getAgentRuntimeConfig,
|
|
213
|
+
pluginRuntime: runtimeConfig.pluginRuntime,
|
|
214
|
+
}),
|
|
215
|
+
ThreadConfigLive({ agentRoster: runtimeConfig.agents.roster, config: runtimeConfig.threads }),
|
|
216
|
+
RuntimeExtensionsLive({}),
|
|
217
|
+
DatabaseLive({
|
|
218
|
+
url: runtimeConfig.database.url,
|
|
219
|
+
namespace: runtimeConfig.database.namespace,
|
|
220
|
+
database: LOTA_SDK_DATABASE_NAME,
|
|
221
|
+
username: runtimeConfig.database.username,
|
|
222
|
+
password: runtimeConfig.database.password,
|
|
223
|
+
}),
|
|
224
|
+
RedisLive({ url: runtimeConfig.redis.url }),
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
return QueueJobServiceLive.pipe(Layer.provideMerge(infrastructureLayer))
|
|
228
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ManagedRuntime } from 'effect'
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line typescript-eslint/no-explicit-any -- ManagedRuntime is contravariant in R; `any` is the only valid wildcard
|
|
4
|
+
type SdkManagedRuntime = ManagedRuntime.ManagedRuntime<any, any>
|
|
5
|
+
|
|
6
|
+
let currentRuntime: SdkManagedRuntime | null = null
|
|
7
|
+
|
|
8
|
+
export function setCurrentRuntime<R, E>(runtime: ManagedRuntime.ManagedRuntime<R, E>): void {
|
|
9
|
+
currentRuntime = runtime as SdkManagedRuntime
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function clearCurrentRuntime(): void {
|
|
13
|
+
currentRuntime = null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getCurrentRuntime(): SdkManagedRuntime {
|
|
17
|
+
if (!currentRuntime) {
|
|
18
|
+
throw new Error('Lota Effect runtime is not configured. Call createLotaRuntime() first.')
|
|
19
|
+
}
|
|
20
|
+
return currentRuntime
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getOptionalCurrentRuntime(): SdkManagedRuntime | null {
|
|
24
|
+
return currentRuntime
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Effect, ManagedRuntime } from 'effect'
|
|
2
|
+
|
|
3
|
+
import { clearCurrentRuntime, getCurrentRuntime, getOptionalCurrentRuntime, setCurrentRuntime } from './runtime-ref'
|
|
4
|
+
|
|
5
|
+
export function setLotaSdkRuntime<R, E>(runtime: ManagedRuntime.ManagedRuntime<R, E>): void {
|
|
6
|
+
setCurrentRuntime(runtime)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function clearLotaSdkRuntime(): void {
|
|
10
|
+
clearCurrentRuntime()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getOptionalLotaSdkRuntime() {
|
|
14
|
+
return getOptionalCurrentRuntime()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Returns the current managed runtime or throws. */
|
|
18
|
+
export function getLotaSdkRuntime() {
|
|
19
|
+
return getCurrentRuntime()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Run an effect as a Promise using the current managed runtime's context.
|
|
24
|
+
* The runtime must be initialized via `createLotaRuntime()` before any call.
|
|
25
|
+
*/
|
|
26
|
+
export function runPromise<A, E>(
|
|
27
|
+
effect: Effect.Effect<A, E, never>,
|
|
28
|
+
options?: { readonly signal?: AbortSignal },
|
|
29
|
+
): Promise<A> {
|
|
30
|
+
return getCurrentRuntime().runPromise(effect, options)
|
|
31
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ToolSet } from 'ai'
|
|
2
|
+
import { Context } from 'effect'
|
|
3
|
+
|
|
4
|
+
import type { ResolvedAgentConfig } from '../config/agent-defaults'
|
|
5
|
+
import type { AgentFactory, AgentRuntimeConfigProvider, AgentToolBuilder } from '../config/agent-types'
|
|
6
|
+
import type { LotaLoggerSet } from '../config/logger'
|
|
7
|
+
import type { ResolvedThreadBootstrapConfig } from '../config/thread-defaults'
|
|
8
|
+
import type { SurrealDBService } from '../db/service'
|
|
9
|
+
import type { RedisConnectionManager } from '../redis/connection'
|
|
10
|
+
import type { ResolvedLotaRuntimeConfig } from '../runtime/runtime-config'
|
|
11
|
+
import type { LotaRuntimeAdapters, LotaRuntimeTurnHooks } from '../runtime/runtime-extensions'
|
|
12
|
+
import type { LotaRuntimeWorkerExtensions } from '../runtime/runtime-worker-registry'
|
|
13
|
+
|
|
14
|
+
export class DatabaseServiceTag extends Context.Service<DatabaseServiceTag, SurrealDBService>()('DatabaseService') {}
|
|
15
|
+
|
|
16
|
+
export class RedisServiceTag extends Context.Service<RedisServiceTag, RedisConnectionManager>()('RedisService') {}
|
|
17
|
+
|
|
18
|
+
export class AppLoggerTag extends Context.Service<AppLoggerTag, LotaLoggerSet>()('AppLogger') {}
|
|
19
|
+
|
|
20
|
+
export class RuntimeConfigServiceTag extends Context.Service<RuntimeConfigServiceTag, ResolvedLotaRuntimeConfig>()(
|
|
21
|
+
'RuntimeConfigService',
|
|
22
|
+
) {}
|
|
23
|
+
|
|
24
|
+
export class AgentConfigServiceTag extends Context.Service<AgentConfigServiceTag, ResolvedAgentConfig>()(
|
|
25
|
+
'AgentConfigService',
|
|
26
|
+
) {}
|
|
27
|
+
|
|
28
|
+
export class AgentFactoryServiceTag extends Context.Service<
|
|
29
|
+
AgentFactoryServiceTag,
|
|
30
|
+
{
|
|
31
|
+
readonly createAgent: AgentFactory
|
|
32
|
+
readonly buildAgentTools: AgentToolBuilder
|
|
33
|
+
readonly getAgentRuntimeConfig: AgentRuntimeConfigProvider
|
|
34
|
+
readonly pluginRuntime?: Record<string, unknown>
|
|
35
|
+
}
|
|
36
|
+
>()('AgentFactoryService') {}
|
|
37
|
+
|
|
38
|
+
export class ThreadConfigServiceTag extends Context.Service<ThreadConfigServiceTag, ResolvedThreadBootstrapConfig>()(
|
|
39
|
+
'ThreadConfigService',
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
export class RuntimeAdaptersServiceTag extends Context.Service<RuntimeAdaptersServiceTag, LotaRuntimeAdapters>()(
|
|
43
|
+
'RuntimeAdaptersService',
|
|
44
|
+
) {}
|
|
45
|
+
|
|
46
|
+
export class TurnHooksServiceTag extends Context.Service<TurnHooksServiceTag, LotaRuntimeTurnHooks>()(
|
|
47
|
+
'TurnHooksService',
|
|
48
|
+
) {}
|
|
49
|
+
|
|
50
|
+
export class ToolProvidersServiceTag extends Context.Service<ToolProvidersServiceTag, ToolSet>()(
|
|
51
|
+
'ToolProvidersService',
|
|
52
|
+
) {}
|
|
53
|
+
|
|
54
|
+
export class RuntimeWorkerExtensionsServiceTag extends Context.Service<
|
|
55
|
+
RuntimeWorkerExtensionsServiceTag,
|
|
56
|
+
LotaRuntimeWorkerExtensions
|
|
57
|
+
>()('RuntimeWorkerExtensionsService') {}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Effect } from 'effect'
|
|
2
|
+
import { ZodError } from 'zod'
|
|
3
|
+
import type { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
import { ValidationError } from './errors'
|
|
6
|
+
import type { ValidationIssue } from './errors'
|
|
7
|
+
|
|
8
|
+
type ZodIssueLike = { readonly path: ReadonlyArray<PropertyKey>; readonly message: string }
|
|
9
|
+
|
|
10
|
+
function toValidationIssuePath(path: ReadonlyArray<PropertyKey>): string {
|
|
11
|
+
return path.map((segment) => String(segment)).join('.') || 'root'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getZodIssues(error: Pick<ZodError, 'issues'> | ReadonlyArray<ZodIssueLike>): ReadonlyArray<ZodIssueLike> {
|
|
15
|
+
return 'issues' in error ? error.issues : error
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function toValidationIssues(
|
|
19
|
+
error: Pick<ZodError, 'issues'> | ReadonlyArray<ZodIssueLike>,
|
|
20
|
+
): ReadonlyArray<ValidationIssue> {
|
|
21
|
+
return getZodIssues(error).map((issue) => ({ path: toValidationIssuePath(issue.path), message: issue.message }))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function summarizeZodIssues(issues: ReadonlyArray<ZodIssueLike>, maxIssues = 5): string {
|
|
25
|
+
return issues
|
|
26
|
+
.slice(0, maxIssues)
|
|
27
|
+
.map((issue) => `${toValidationIssuePath(issue.path)}: ${issue.message}`)
|
|
28
|
+
.join('; ')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function toValidationError(error: unknown, message?: string): ValidationError {
|
|
32
|
+
return new ValidationError({
|
|
33
|
+
message: message ?? (error instanceof ZodError ? 'Schema validation failed' : 'Validation failed'),
|
|
34
|
+
issues: error instanceof ZodError ? toValidationIssues(error) : undefined,
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function zodParse<TSchema extends z.ZodTypeAny>(
|
|
39
|
+
schema: TSchema,
|
|
40
|
+
value: unknown,
|
|
41
|
+
): Effect.Effect<z.infer<TSchema>, ValidationError> {
|
|
42
|
+
return Effect.try({ try: () => schema.parse(value), catch: toValidationError })
|
|
43
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { embed, embedMany } from 'ai'
|
|
2
|
+
import { Schema, Effect } from 'effect'
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { ConfigurationError } from '../effect/errors'
|
|
5
|
+
import { getOptionalCurrentRuntime } from '../effect/runtime-ref'
|
|
6
|
+
import { getDirectOpenRouterProvider, normalizeDirectOpenRouterModelId } from '../openrouter/direct-provider'
|
|
6
7
|
|
|
7
8
|
const SUPPORTED_EMBEDDING_PREFIXES = ['openai/', 'openrouter/'] as const
|
|
8
9
|
|
|
@@ -15,85 +16,129 @@ type ProviderEmbeddingsOptions = {
|
|
|
15
16
|
embedFn?: typeof embed
|
|
16
17
|
embedManyFn?: typeof embedMany
|
|
17
18
|
getCache?: () => SharedEmbeddingCache | null
|
|
18
|
-
modelId
|
|
19
|
+
modelId: string
|
|
20
|
+
openRouterApiKey?: string
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
function resolveEmbeddingModel(modelId: string) {
|
|
23
|
+
function resolveEmbeddingModel(modelId: string, openRouterApiKey?: string) {
|
|
22
24
|
const normalized = modelId.trim()
|
|
23
25
|
if (!normalized) {
|
|
24
|
-
throw new
|
|
26
|
+
throw new ConfigurationError({ message: '[embeddings-provider] Model id is required.', key: 'embeddingModelId' })
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
if (!SUPPORTED_EMBEDDING_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
28
|
-
throw new
|
|
29
|
-
`[embeddings-provider] Unsupported model id "${modelId}". Use one of: ${SUPPORTED_EMBEDDING_PREFIXES.join(', ')}*.`,
|
|
30
|
-
|
|
30
|
+
throw new ConfigurationError({
|
|
31
|
+
message: `[embeddings-provider] Unsupported model id "${modelId}". Use one of: ${SUPPORTED_EMBEDDING_PREFIXES.join(', ')}*.`,
|
|
32
|
+
key: 'embeddingModelId',
|
|
33
|
+
})
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
return
|
|
36
|
+
return getDirectOpenRouterProvider(openRouterApiKey).embeddingModel(normalizeDirectOpenRouterModelId(normalized))
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
function normalizeEmbedding(embedding: readonly number[]): number[] {
|
|
37
40
|
return embedding.map((value) => Number(value))
|
|
38
41
|
}
|
|
39
42
|
|
|
43
|
+
class EmbeddingProviderError extends Schema.TaggedErrorClass<EmbeddingProviderError>()('EmbeddingProviderError', {
|
|
44
|
+
message: Schema.String,
|
|
45
|
+
cause: Schema.Defect,
|
|
46
|
+
}) {}
|
|
47
|
+
|
|
48
|
+
function tryEmbeddingPromise<A>(
|
|
49
|
+
message: string,
|
|
50
|
+
thunk: () => PromiseLike<A>,
|
|
51
|
+
): Effect.Effect<A, EmbeddingProviderError> {
|
|
52
|
+
return Effect.tryPromise({
|
|
53
|
+
try: () => Promise.resolve(thunk()),
|
|
54
|
+
catch: (cause) => new EmbeddingProviderError({ message, cause }),
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
40
58
|
export class ProviderEmbeddings {
|
|
41
59
|
private readonly embedFn: typeof embed
|
|
42
60
|
private readonly embedManyFn: typeof embedMany
|
|
43
61
|
private readonly getCache: () => SharedEmbeddingCache | null
|
|
44
|
-
private readonly
|
|
45
|
-
private resolvedModelId: string | null = null
|
|
62
|
+
private readonly resolvedModelId: string
|
|
46
63
|
private _model: ReturnType<typeof resolveEmbeddingModel> | null = null
|
|
64
|
+
/** In-flight dedup: concurrent embedQuery calls for the same text share one API round-trip. */
|
|
65
|
+
private readonly inflightEmbeddings = new Map<string, Promise<number[]>>()
|
|
66
|
+
|
|
67
|
+
private readonly openRouterApiKey: string | undefined
|
|
47
68
|
|
|
48
|
-
constructor(options: ProviderEmbeddingsOptions
|
|
69
|
+
constructor(options: ProviderEmbeddingsOptions) {
|
|
49
70
|
this.embedFn = options.embedFn ?? embed
|
|
50
71
|
this.embedManyFn = options.embedManyFn ?? embedMany
|
|
51
|
-
this.getCache = options.getCache ??
|
|
52
|
-
this.
|
|
72
|
+
this.getCache = options.getCache ?? (() => null)
|
|
73
|
+
this.resolvedModelId = options.modelId
|
|
74
|
+
this.openRouterApiKey = options.openRouterApiKey
|
|
53
75
|
}
|
|
54
76
|
|
|
55
77
|
private getModelId(): string {
|
|
56
|
-
if (!this.resolvedModelId) {
|
|
57
|
-
this.resolvedModelId = this.configuredModelId ?? getRuntimeConfig().aiGateway.embeddingModel
|
|
58
|
-
}
|
|
59
|
-
|
|
60
78
|
return this.resolvedModelId
|
|
61
79
|
}
|
|
62
80
|
|
|
63
81
|
private getModel() {
|
|
64
82
|
if (!this._model) {
|
|
65
|
-
this._model = resolveEmbeddingModel(this.getModelId())
|
|
83
|
+
this._model = resolveEmbeddingModel(this.getModelId(), this.openRouterApiKey)
|
|
66
84
|
}
|
|
67
85
|
return this._model
|
|
68
86
|
}
|
|
69
87
|
|
|
70
|
-
private
|
|
88
|
+
private loadCachedEmbedding(text: string): Promise<number[] | null> {
|
|
71
89
|
const redisCache = this.getCache()
|
|
72
|
-
if (!redisCache) return null
|
|
90
|
+
if (!redisCache) return Promise.resolve(null)
|
|
73
91
|
|
|
74
92
|
return redisCache.get(this.getModelId(), text)
|
|
75
93
|
}
|
|
76
94
|
|
|
77
|
-
|
|
95
|
+
private runEffect<A>(effect: Effect.Effect<A, EmbeddingProviderError>): Promise<A> {
|
|
96
|
+
const runtime = getOptionalCurrentRuntime()
|
|
97
|
+
return runtime ? runtime.runPromise(effect) : Effect.runPromise(effect)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
embedQuery(text: string): Promise<number[]> {
|
|
78
101
|
const input = text.trim()
|
|
79
|
-
if (!input) return []
|
|
102
|
+
if (!input) return Promise.resolve([])
|
|
80
103
|
|
|
81
|
-
const
|
|
82
|
-
|
|
104
|
+
const dedupKey = `${this.getModelId()}:${input}`
|
|
105
|
+
const pending = this.inflightEmbeddings.get(dedupKey)
|
|
106
|
+
if (pending) return pending
|
|
83
107
|
|
|
84
|
-
const
|
|
85
|
-
|
|
108
|
+
const promise = this.runEffect(this.executeEmbedQueryEffect(input))
|
|
109
|
+
this.inflightEmbeddings.set(dedupKey, promise)
|
|
110
|
+
void promise.finally(() => this.inflightEmbeddings.delete(dedupKey))
|
|
86
111
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
112
|
+
return promise
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private executeEmbedQueryEffect(input: string): Effect.Effect<number[], EmbeddingProviderError> {
|
|
116
|
+
const self = this
|
|
117
|
+
|
|
118
|
+
return Effect.gen(function* () {
|
|
119
|
+
const cached = yield* tryEmbeddingPromise('Failed to load cached query embedding.', () =>
|
|
120
|
+
self.loadCachedEmbedding(input),
|
|
121
|
+
)
|
|
122
|
+
if (cached) {
|
|
123
|
+
return cached
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const result = yield* tryEmbeddingPromise('Failed to generate query embedding.', () =>
|
|
127
|
+
self.embedFn({ model: self.getModel(), value: input, maxRetries: 2 }),
|
|
128
|
+
)
|
|
129
|
+
const embedding = normalizeEmbedding(result.embedding)
|
|
130
|
+
|
|
131
|
+
const redisCache = self.getCache()
|
|
132
|
+
if (redisCache) {
|
|
133
|
+
void redisCache.set(self.getModelId(), input, embedding)
|
|
134
|
+
}
|
|
91
135
|
|
|
92
|
-
|
|
136
|
+
return embedding
|
|
137
|
+
}).pipe(Effect.withSpan('ProviderEmbeddings.executeEmbedQuery'))
|
|
93
138
|
}
|
|
94
139
|
|
|
95
|
-
|
|
96
|
-
if (values.length === 0) return []
|
|
140
|
+
embedDocuments(values: string[]): Promise<number[][]> {
|
|
141
|
+
if (values.length === 0) return Promise.resolve([])
|
|
97
142
|
|
|
98
143
|
const normalized = values.map((value) => value.trim())
|
|
99
144
|
const nonEmptyEntries = normalized
|
|
@@ -101,56 +146,62 @@ export class ProviderEmbeddings {
|
|
|
101
146
|
.filter((entry) => entry.value.length > 0)
|
|
102
147
|
|
|
103
148
|
if (nonEmptyEntries.length === 0) {
|
|
104
|
-
return normalized.map(() => [])
|
|
149
|
+
return Promise.resolve(normalized.map(() => []))
|
|
105
150
|
}
|
|
106
151
|
|
|
107
152
|
const uniqueTexts = [...new Set(nonEmptyEntries.map((entry) => entry.value))]
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const redisCache = this.getCache()
|
|
112
|
-
if (redisCache && missingTexts.length > 0) {
|
|
113
|
-
const redisResults = await Promise.all(
|
|
114
|
-
missingTexts.map(async (text) => ({ text, embedding: await redisCache.get(this.getModelId(), text) })),
|
|
115
|
-
)
|
|
153
|
+
return this.runEffect(this.embedDocumentsEffect(normalized, uniqueTexts))
|
|
154
|
+
}
|
|
116
155
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
156
|
+
private embedDocumentsEffect(
|
|
157
|
+
normalized: string[],
|
|
158
|
+
uniqueTexts: string[],
|
|
159
|
+
): Effect.Effect<number[][], EmbeddingProviderError> {
|
|
160
|
+
const self = this
|
|
161
|
+
|
|
162
|
+
return Effect.gen(function* () {
|
|
163
|
+
const embeddingsByText = new Map<string, number[]>()
|
|
164
|
+
let missingTexts = [...uniqueTexts]
|
|
165
|
+
const redisCache = self.getCache()
|
|
166
|
+
const redisResults =
|
|
167
|
+
redisCache && missingTexts.length > 0
|
|
168
|
+
? yield* Effect.all(
|
|
169
|
+
missingTexts.map((text) =>
|
|
170
|
+
tryEmbeddingPromise('Failed to load cached document embedding.', () =>
|
|
171
|
+
redisCache.get(self.getModelId(), text),
|
|
172
|
+
).pipe(Effect.map((embedding) => ({ text, embedding }))),
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
: ([] as Array<{ text: string; embedding: number[] | null }>)
|
|
176
|
+
|
|
177
|
+
if (redisCache && missingTexts.length > 0) {
|
|
178
|
+
missingTexts = []
|
|
179
|
+
for (const result of redisResults) {
|
|
180
|
+
if (!result.embedding) {
|
|
181
|
+
missingTexts.push(result.text)
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
embeddingsByText.set(result.text, result.embedding)
|
|
122
186
|
}
|
|
123
|
-
|
|
124
|
-
embeddingsByText.set(result.text, result.embedding)
|
|
125
187
|
}
|
|
126
|
-
}
|
|
127
188
|
|
|
128
|
-
|
|
129
|
-
|
|
189
|
+
if (missingTexts.length === 0) {
|
|
190
|
+
return normalized.map((text) => (text ? (embeddingsByText.get(text) ?? []) : []))
|
|
191
|
+
}
|
|
130
192
|
|
|
193
|
+
const result = yield* tryEmbeddingPromise('Failed to generate document embeddings.', () =>
|
|
194
|
+
self.embedManyFn({ model: self.getModel(), values: missingTexts, maxRetries: 2 }),
|
|
195
|
+
)
|
|
131
196
|
missingTexts.forEach((text, index) => {
|
|
132
197
|
const embedding = normalizeEmbedding(result.embeddings[index] ?? [])
|
|
133
198
|
embeddingsByText.set(text, embedding)
|
|
134
199
|
if (redisCache) {
|
|
135
|
-
void redisCache.set(
|
|
200
|
+
void redisCache.set(self.getModelId(), text, embedding)
|
|
136
201
|
}
|
|
137
202
|
})
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return normalized.map((text) => (text ? (embeddingsByText.get(text) ?? []) : []))
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
203
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
export function getDefaultEmbeddings(): ProviderEmbeddings {
|
|
147
|
-
if (!defaultEmbeddings) {
|
|
148
|
-
defaultEmbeddings = new ProviderEmbeddings()
|
|
204
|
+
return normalized.map((text) => (text ? (embeddingsByText.get(text) ?? []) : []))
|
|
205
|
+
}).pipe(Effect.withSpan('ProviderEmbeddings.embedDocuments'))
|
|
149
206
|
}
|
|
150
|
-
|
|
151
|
-
return defaultEmbeddings
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export function resetDefaultEmbeddingsForTests(): void {
|
|
155
|
-
defaultEmbeddings = null
|
|
156
207
|
}
|