@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
|
@@ -1,28 +1,23 @@
|
|
|
1
1
|
import { recordIdStringSchema } from '@lota-sdk/shared'
|
|
2
|
+
import { Cache, Context, Schema, Duration, Effect, Layer } from 'effect'
|
|
2
3
|
import { BoundQuery } from 'surrealdb'
|
|
3
4
|
import { z } from 'zod'
|
|
4
5
|
|
|
5
6
|
import { renderLearnedSkillInstructions } from '../ai/definitions'
|
|
6
|
-
import { lotaDebugLogger } from '../config/debug-logger'
|
|
7
7
|
import { serverLogger } from '../config/logger'
|
|
8
8
|
import { ensureRecordId, recordIdToString } from '../db/record-id'
|
|
9
|
-
import {
|
|
9
|
+
import type { SurrealDBService } from '../db/service'
|
|
10
10
|
import { TABLES } from '../db/tables'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
import { DatabaseServiceTag, RuntimeConfigServiceTag } from '../effect/services'
|
|
12
|
+
import { ProviderEmbeddings } from '../embeddings/provider'
|
|
13
|
+
import { sha256HexFromParts } from '../utils/crypto'
|
|
14
|
+
import { nowDate } from '../utils/date-time'
|
|
15
15
|
|
|
16
16
|
const PROMOTION_MIN_USES = 5
|
|
17
17
|
const PROMOTION_MIN_SUCCESS_RATE = 0.6
|
|
18
18
|
|
|
19
19
|
const ACTIVE_SKILL_FILTER = "AND status IN ['learned', 'verified', 'promoted'] AND archivedAt IS NONE"
|
|
20
20
|
const SKILL_EXISTS_TTL_SECONDS = 120
|
|
21
|
-
const SKILL_EXISTS_KEY_PREFIX = 'skill-exists'
|
|
22
|
-
|
|
23
|
-
function skillExistsKey(orgId: string, agentId: string): string {
|
|
24
|
-
return `${SKILL_EXISTS_KEY_PREFIX}:${orgId}:${agentId}`
|
|
25
|
-
}
|
|
26
21
|
|
|
27
22
|
const LearnedSkillRowSchema = z.object({
|
|
28
23
|
id: recordIdStringSchema,
|
|
@@ -59,7 +54,36 @@ const SearchResultRowSchema = z.object({
|
|
|
59
54
|
similarity: z.number(),
|
|
60
55
|
})
|
|
61
56
|
|
|
62
|
-
|
|
57
|
+
class LearnedSkillServiceError extends Schema.TaggedErrorClass<LearnedSkillServiceError>()('LearnedSkillServiceError', {
|
|
58
|
+
message: Schema.String,
|
|
59
|
+
cause: Schema.Defect,
|
|
60
|
+
}) {}
|
|
61
|
+
|
|
62
|
+
class LearnedSkillNotFoundError extends Schema.TaggedErrorClass<LearnedSkillNotFoundError>()(
|
|
63
|
+
'LearnedSkillNotFoundError',
|
|
64
|
+
{ message: Schema.String },
|
|
65
|
+
) {}
|
|
66
|
+
|
|
67
|
+
function tryLearnedSkillPromise<A>(
|
|
68
|
+
message: string,
|
|
69
|
+
thunk: () => PromiseLike<A> | Effect.Effect<A, unknown>,
|
|
70
|
+
): Effect.Effect<A, LearnedSkillServiceError> {
|
|
71
|
+
return Effect.suspend(() => {
|
|
72
|
+
try {
|
|
73
|
+
const value = thunk()
|
|
74
|
+
if (Effect.isEffect(value)) {
|
|
75
|
+
return value.pipe(Effect.mapError((cause) => new LearnedSkillServiceError({ message, cause })))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return Effect.tryPromise({
|
|
79
|
+
try: () => Promise.resolve(value),
|
|
80
|
+
catch: (cause) => new LearnedSkillServiceError({ message, cause }),
|
|
81
|
+
})
|
|
82
|
+
} catch (cause) {
|
|
83
|
+
return Effect.fail(new LearnedSkillServiceError({ message, cause }))
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
}
|
|
63
87
|
|
|
64
88
|
interface CreateLearnedSkillInput {
|
|
65
89
|
name: string
|
|
@@ -98,124 +122,101 @@ interface RetrieveForTurnParams {
|
|
|
98
122
|
minConfidence: number
|
|
99
123
|
}
|
|
100
124
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
{
|
|
184
|
-
)
|
|
125
|
+
export function makeLearnedSkillService(
|
|
126
|
+
db: SurrealDBService,
|
|
127
|
+
options: { embeddingModel: string; openRouterApiKey?: string },
|
|
128
|
+
skillExistsCache: Cache.Cache<string, boolean, LearnedSkillServiceError>,
|
|
129
|
+
) {
|
|
130
|
+
const embeddings = new ProviderEmbeddings({
|
|
131
|
+
modelId: options.embeddingModel,
|
|
132
|
+
openRouterApiKey: options.openRouterApiKey,
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const hasSkillsForAgent = (orgId: string, agentId: string) => Cache.get(skillExistsCache, `${orgId}:${agentId}`)
|
|
136
|
+
|
|
137
|
+
const invalidateSkillExistsCache = (orgId: string, agentId: string | null) =>
|
|
138
|
+
Effect.gen(function* () {
|
|
139
|
+
const keys = yield* Cache.keys(skillExistsCache)
|
|
140
|
+
const orgPrefix = `${orgId}:`
|
|
141
|
+
const matchingKeys = [...keys].filter((key) => key.startsWith(orgPrefix))
|
|
142
|
+
if (matchingKeys.length > 0) {
|
|
143
|
+
yield* Effect.forEach(matchingKeys, (key) => Cache.invalidate(skillExistsCache, key))
|
|
144
|
+
}
|
|
145
|
+
if (agentId) {
|
|
146
|
+
yield* Cache.set(skillExistsCache, `${orgId}:${agentId}`, true)
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
const create = (input: CreateLearnedSkillInput) =>
|
|
151
|
+
Effect.gen(function* () {
|
|
152
|
+
const orgRef = ensureRecordId(input.organizationId, TABLES.ORGANIZATION)
|
|
153
|
+
|
|
154
|
+
const data: Record<string, unknown> = {
|
|
155
|
+
name: input.name,
|
|
156
|
+
description: input.description,
|
|
157
|
+
instructions: input.instructions,
|
|
158
|
+
triggers: input.triggers,
|
|
159
|
+
tags: input.tags,
|
|
160
|
+
examples: input.examples,
|
|
161
|
+
sourceType: input.sourceType,
|
|
162
|
+
organizationId: orgRef,
|
|
163
|
+
agentId: input.agentId,
|
|
164
|
+
confidence: input.confidence,
|
|
165
|
+
embedding: input.embedding,
|
|
166
|
+
hash: input.hash,
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const result = yield* tryLearnedSkillPromise('Failed to create learned skill.', () =>
|
|
170
|
+
db.create(TABLES.LEARNED_SKILL, data, LearnedSkillRowSchema),
|
|
171
|
+
)
|
|
172
|
+
yield* invalidateSkillExistsCache(input.organizationId, input.agentId)
|
|
173
|
+
return result
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
const update = (skillId: string, input: UpdateLearnedSkillInput) =>
|
|
177
|
+
Effect.gen(function* () {
|
|
178
|
+
const ref = ensureRecordId(skillId, TABLES.LEARNED_SKILL)
|
|
179
|
+
const data: Record<string, unknown> = {}
|
|
180
|
+
|
|
181
|
+
if (input.name !== undefined) data.name = input.name
|
|
182
|
+
if (input.description !== undefined) data.description = input.description
|
|
183
|
+
if (input.instructions !== undefined) data.instructions = input.instructions
|
|
184
|
+
if (input.triggers !== undefined) data.triggers = input.triggers
|
|
185
|
+
if (input.tags !== undefined) data.tags = input.tags
|
|
186
|
+
if (input.examples !== undefined) data.examples = input.examples
|
|
187
|
+
if (input.confidence !== undefined) data.confidence = input.confidence
|
|
188
|
+
if (input.version !== undefined) data.version = input.version
|
|
189
|
+
if (input.embedding !== undefined) data.embedding = input.embedding
|
|
190
|
+
if (input.hash !== undefined) data.hash = input.hash
|
|
191
|
+
if (input.supersedes !== undefined) data.supersedes = ensureRecordId(input.supersedes, TABLES.LEARNED_SKILL)
|
|
192
|
+
|
|
193
|
+
const updated = yield* tryLearnedSkillPromise(`Failed to update learned skill ${skillId}.`, () =>
|
|
194
|
+
db.update(TABLES.LEARNED_SKILL, ref, data, LearnedSkillRowSchema),
|
|
195
|
+
)
|
|
196
|
+
if (!updated) {
|
|
197
|
+
return yield* new LearnedSkillNotFoundError({ message: `Learned skill ${skillId} not found` })
|
|
198
|
+
}
|
|
199
|
+
return updated
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const archive = (skillId: string) =>
|
|
203
|
+
Effect.gen(function* () {
|
|
204
|
+
const ref = ensureRecordId(skillId, TABLES.LEARNED_SKILL)
|
|
205
|
+
const skill = yield* getById(skillId)
|
|
206
|
+
yield* tryLearnedSkillPromise(`Failed to archive learned skill ${skillId}.`, () =>
|
|
207
|
+
db.update(TABLES.LEARNED_SKILL, ref, { status: 'archived', archivedAt: nowDate() }, LearnedSkillRowSchema),
|
|
208
|
+
)
|
|
209
|
+
if (skill) {
|
|
210
|
+
yield* invalidateSkillExistsCache(recordIdToString(skill.organizationId), skill.agentId ?? null)
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
const getById = (skillId: string): Effect.Effect<LearnedSkillRow | null, LearnedSkillServiceError> =>
|
|
215
|
+
tryLearnedSkillPromise(`Failed to load learned skill ${skillId}.`, () =>
|
|
216
|
+
db.findOne(TABLES.LEARNED_SKILL, { id: ensureRecordId(skillId, TABLES.LEARNED_SKILL) }, LearnedSkillRowSchema),
|
|
185
217
|
)
|
|
186
218
|
|
|
187
|
-
|
|
188
|
-
await redis.set(key, exists ? '1' : '0', 'EX', SKILL_EXISTS_TTL_SECONDS)
|
|
189
|
-
return exists
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
private async invalidateSkillExistsCache(orgId: string, agentId: string | null): Promise<void> {
|
|
193
|
-
const redis = getRedisConnection()
|
|
194
|
-
const pattern = `${SKILL_EXISTS_KEY_PREFIX}:${orgId}:*`
|
|
195
|
-
const keys = await redis.keys(pattern)
|
|
196
|
-
if (keys.length > 0) {
|
|
197
|
-
await redis.del(...keys)
|
|
198
|
-
}
|
|
199
|
-
// Also set the specific key if we know a skill was just created
|
|
200
|
-
if (agentId) {
|
|
201
|
-
await redis.set(skillExistsKey(orgId, agentId), '1', 'EX', SKILL_EXISTS_TTL_SECONDS)
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async searchForTurn(params: RetrieveForTurnParams): Promise<SearchResultRow[]> {
|
|
206
|
-
const timer = lotaDebugLogger.timer('learned-skills')
|
|
207
|
-
|
|
208
|
-
const hasSkills = await this.hasSkillsForAgent(params.orgId, params.agentId)
|
|
209
|
-
if (!hasSkills) {
|
|
210
|
-
lotaDebugLogger.step('learned-skills: skipped — no skills for org+agent')
|
|
211
|
-
return []
|
|
212
|
-
}
|
|
213
|
-
timer.step('has-skills-check')
|
|
214
|
-
|
|
215
|
-
const queryEmbedding = await embeddings.embedQuery(params.query)
|
|
216
|
-
timer.step('embed-query')
|
|
217
|
-
if (queryEmbedding.length === 0) return []
|
|
218
|
-
|
|
219
|
+
const searchForTurn = Effect.fn('LearnedSkillService.searchForTurn')(function* (params: RetrieveForTurnParams) {
|
|
219
220
|
const orgRef = ensureRecordId(params.orgId, TABLES.ORGANIZATION)
|
|
220
221
|
const sql = `
|
|
221
222
|
SELECT
|
|
@@ -234,153 +235,257 @@ class LearnedSkillService {
|
|
|
234
235
|
ORDER BY similarity DESC
|
|
235
236
|
`
|
|
236
237
|
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
organizationId: orgRef,
|
|
240
|
-
embedding: queryEmbedding,
|
|
241
|
-
agentId: params.agentId,
|
|
242
|
-
minConfidence: params.minConfidence,
|
|
243
|
-
}),
|
|
238
|
+
const hasSkills = yield* hasSkillsForAgent(params.orgId, params.agentId).pipe(
|
|
239
|
+
Effect.withSpan('LearnedSkillService.checkSkillsAvailability'),
|
|
244
240
|
)
|
|
245
|
-
|
|
241
|
+
yield* Effect.annotateCurrentSpan({
|
|
242
|
+
orgId: params.orgId,
|
|
243
|
+
agentId: params.agentId,
|
|
244
|
+
limit: params.limit,
|
|
245
|
+
minConfidence: params.minConfidence,
|
|
246
|
+
skillsAvailable: hasSkills,
|
|
247
|
+
})
|
|
248
|
+
if (!hasSkills) {
|
|
249
|
+
return []
|
|
250
|
+
}
|
|
246
251
|
|
|
252
|
+
const queryEmbedding = yield* tryLearnedSkillPromise('Failed to embed learned skill query.', () =>
|
|
253
|
+
embeddings.embedQuery(params.query),
|
|
254
|
+
).pipe(Effect.withSpan('LearnedSkillService.embedQuery'))
|
|
255
|
+
yield* Effect.annotateCurrentSpan('embeddingLength', queryEmbedding.length)
|
|
256
|
+
if (queryEmbedding.length === 0) return []
|
|
257
|
+
|
|
258
|
+
const rows = yield* tryLearnedSkillPromise('Failed to query learned skills.', () =>
|
|
259
|
+
db.query<unknown>(
|
|
260
|
+
new BoundQuery(sql, {
|
|
261
|
+
organizationId: orgRef,
|
|
262
|
+
embedding: queryEmbedding,
|
|
263
|
+
agentId: params.agentId,
|
|
264
|
+
minConfidence: params.minConfidence,
|
|
265
|
+
}),
|
|
266
|
+
),
|
|
267
|
+
).pipe(Effect.withSpan('LearnedSkillService.queryNearestSkills'))
|
|
247
268
|
return rows.map((row) => SearchResultRowSchema.parse(row)).filter((row) => row.similarity >= 0.3)
|
|
248
|
-
}
|
|
269
|
+
})
|
|
249
270
|
|
|
250
|
-
|
|
251
|
-
const results =
|
|
271
|
+
const retrieveForTurn = Effect.fn('LearnedSkillService.retrieveForTurn')(function* (params: RetrieveForTurnParams) {
|
|
272
|
+
const results = yield* searchForTurn(params)
|
|
252
273
|
if (results.length === 0) return undefined
|
|
253
274
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
275
|
+
const currentContext = yield* Effect.context<never>()
|
|
276
|
+
yield* Effect.sync(() => {
|
|
277
|
+
void Effect.runForkWith(currentContext)(
|
|
278
|
+
Effect.forEach(
|
|
279
|
+
results,
|
|
280
|
+
(result) =>
|
|
281
|
+
recordUsage(result.id).pipe(
|
|
282
|
+
Effect.tapError((error) =>
|
|
283
|
+
Effect.sync(() => {
|
|
284
|
+
serverLogger.warn`Failed to record learned skill usage for ${result.id}: ${error}`
|
|
285
|
+
}),
|
|
286
|
+
),
|
|
287
|
+
),
|
|
288
|
+
{ discard: true },
|
|
289
|
+
),
|
|
290
|
+
)
|
|
291
|
+
})
|
|
259
292
|
|
|
260
293
|
const section = renderLearnedSkillInstructions(
|
|
261
294
|
results.map((row) => ({ name: row.name, instructions: row.instructions })),
|
|
262
295
|
)
|
|
263
296
|
return section || undefined
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
const recordUsage = (skillId: string) =>
|
|
300
|
+
Effect.gen(function* () {
|
|
301
|
+
const ref = ensureRecordId(skillId, TABLES.LEARNED_SKILL)
|
|
302
|
+
yield* tryLearnedSkillPromise(`Failed to record usage for learned skill ${skillId}.`, () =>
|
|
303
|
+
db.query<unknown>(
|
|
304
|
+
new BoundQuery(
|
|
305
|
+
`UPDATE ${TABLES.LEARNED_SKILL} SET usageCount += 1, lastUsedAt = time::now() WHERE id = $id`,
|
|
306
|
+
{ id: ref },
|
|
307
|
+
),
|
|
308
|
+
),
|
|
309
|
+
)
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
const recordSuccess = (skillId: string) =>
|
|
313
|
+
Effect.gen(function* () {
|
|
314
|
+
const ref = ensureRecordId(skillId, TABLES.LEARNED_SKILL)
|
|
315
|
+
yield* tryLearnedSkillPromise(`Failed to record success for learned skill ${skillId}.`, () =>
|
|
316
|
+
db.query<unknown>(
|
|
317
|
+
new BoundQuery(`UPDATE ${TABLES.LEARNED_SKILL} SET successCount += 1 WHERE id = $id`, { id: ref }),
|
|
318
|
+
),
|
|
319
|
+
)
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
const promoteIfEligible = (skillId: string) =>
|
|
323
|
+
Effect.gen(function* () {
|
|
324
|
+
const skill = yield* getById(skillId)
|
|
325
|
+
if (!skill) return false
|
|
326
|
+
if (skill.status !== 'learned') return false
|
|
327
|
+
if (skill.usageCount < PROMOTION_MIN_USES) return false
|
|
328
|
+
|
|
329
|
+
const successRate = skill.successCount / skill.usageCount
|
|
330
|
+
if (successRate < PROMOTION_MIN_SUCCESS_RATE) return false
|
|
331
|
+
|
|
332
|
+
const ref = ensureRecordId(skillId, TABLES.LEARNED_SKILL)
|
|
333
|
+
yield* tryLearnedSkillPromise(`Failed to promote learned skill ${skillId}.`, () =>
|
|
334
|
+
db.update(
|
|
335
|
+
TABLES.LEARNED_SKILL,
|
|
336
|
+
ref,
|
|
337
|
+
{ status: 'verified', confidence: Math.min(skill.confidence + 0.1, 1.0) },
|
|
338
|
+
LearnedSkillRowSchema,
|
|
339
|
+
),
|
|
340
|
+
)
|
|
341
|
+
return true
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
const findMostSimilar = (orgId: string, description: string) =>
|
|
345
|
+
Effect.gen(function* () {
|
|
346
|
+
const orgRef = ensureRecordId(orgId, TABLES.ORGANIZATION)
|
|
347
|
+
const sql = `
|
|
348
|
+
SELECT *,
|
|
349
|
+
type::string(id) AS id,
|
|
350
|
+
type::string(organizationId) AS organizationId,
|
|
351
|
+
vector::similarity::cosine(embedding, $embedding) AS similarity
|
|
352
|
+
FROM ${TABLES.LEARNED_SKILL}
|
|
353
|
+
WHERE organizationId = $organizationId
|
|
354
|
+
${ACTIVE_SKILL_FILTER}
|
|
355
|
+
AND embedding <|3|> $embedding
|
|
356
|
+
ORDER BY similarity DESC
|
|
357
|
+
LIMIT 1
|
|
358
|
+
`
|
|
359
|
+
|
|
360
|
+
const descEmbedding = yield* tryLearnedSkillPromise('Failed to embed learned skill description.', () =>
|
|
361
|
+
embeddings.embedQuery(description),
|
|
362
|
+
)
|
|
363
|
+
if (descEmbedding.length === 0) return null
|
|
364
|
+
|
|
365
|
+
const rows = yield* tryLearnedSkillPromise('Failed to query most similar learned skill.', () =>
|
|
366
|
+
db.query<unknown>(new BoundQuery(sql, { organizationId: orgRef, embedding: descEmbedding })),
|
|
367
|
+
)
|
|
368
|
+
return rows.length === 0 ? null : LearnedSkillRowSchema.parse(rows[0])
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
const listForOrg = (orgId: string) =>
|
|
372
|
+
tryLearnedSkillPromise(`Failed to list learned skills for organization ${orgId}.`, () =>
|
|
373
|
+
db.queryMany(
|
|
374
|
+
new BoundQuery(
|
|
375
|
+
`SELECT *, type::string(id) AS id, type::string(organizationId) AS organizationId
|
|
376
|
+
FROM ${TABLES.LEARNED_SKILL}
|
|
377
|
+
WHERE organizationId = $organizationId
|
|
378
|
+
${ACTIVE_SKILL_FILTER}
|
|
379
|
+
ORDER BY createdAt DESC`,
|
|
380
|
+
{ organizationId: ensureRecordId(orgId, TABLES.ORGANIZATION) },
|
|
381
|
+
),
|
|
382
|
+
LearnedSkillRowSchema,
|
|
337
383
|
),
|
|
338
|
-
LearnedSkillRowSchema,
|
|
339
384
|
)
|
|
340
|
-
}
|
|
341
385
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
386
|
+
const findByNameOrTag = (orgId: string, nameOrTag: string) =>
|
|
387
|
+
Effect.gen(function* () {
|
|
388
|
+
const orgRef = ensureRecordId(orgId, TABLES.ORGANIZATION)
|
|
389
|
+
const normalizedRef = nameOrTag.trim().toLowerCase()
|
|
390
|
+
const rows = yield* tryLearnedSkillPromise(`Failed to find learned skill by name or tag for org ${orgId}.`, () =>
|
|
391
|
+
db.queryMany(
|
|
392
|
+
new BoundQuery(
|
|
393
|
+
`SELECT *, type::string(id) AS id, type::string(organizationId) AS organizationId
|
|
394
|
+
FROM ${TABLES.LEARNED_SKILL}
|
|
395
|
+
WHERE organizationId = $organizationId
|
|
396
|
+
${ACTIVE_SKILL_FILTER}
|
|
397
|
+
AND (string::lowercase(name) = $nameRef OR $nameRef IN tags)
|
|
398
|
+
ORDER BY confidence DESC
|
|
399
|
+
LIMIT 1`,
|
|
400
|
+
{ organizationId: orgRef, nameRef: normalizedRef },
|
|
401
|
+
),
|
|
402
|
+
LearnedSkillRowSchema,
|
|
403
|
+
),
|
|
404
|
+
)
|
|
405
|
+
return rows[0] ?? null
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
const findByHash = (orgId: string, hash: string): Effect.Effect<LearnedSkillRow | null, LearnedSkillServiceError> =>
|
|
409
|
+
Effect.gen(function* () {
|
|
410
|
+
const orgRef = ensureRecordId(orgId, TABLES.ORGANIZATION)
|
|
411
|
+
const rows = yield* tryLearnedSkillPromise(`Failed to find learned skill by hash for org ${orgId}.`, () =>
|
|
412
|
+
db.queryMany(
|
|
413
|
+
new BoundQuery(
|
|
414
|
+
`SELECT *, type::string(id) AS id, type::string(organizationId) AS organizationId
|
|
415
|
+
FROM ${TABLES.LEARNED_SKILL}
|
|
416
|
+
WHERE organizationId = $organizationId
|
|
417
|
+
AND hash = $hash
|
|
418
|
+
${ACTIVE_SKILL_FILTER}
|
|
419
|
+
LIMIT 1`,
|
|
420
|
+
{ organizationId: orgRef, hash },
|
|
421
|
+
),
|
|
422
|
+
LearnedSkillRowSchema,
|
|
423
|
+
),
|
|
424
|
+
)
|
|
425
|
+
return rows[0] ?? null
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
function generateHash(description: string, instructions: string): string {
|
|
429
|
+
return sha256HexFromParts([description.trim().toLowerCase(), instructions.trim().toLowerCase()])
|
|
347
430
|
}
|
|
348
431
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
)
|
|
365
|
-
return rows[0] ?? null
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
async findByHash(orgId: string, hash: string): Promise<LearnedSkillRow | null> {
|
|
369
|
-
const orgRef = ensureRecordId(orgId, TABLES.ORGANIZATION)
|
|
370
|
-
const rows = await databaseService.queryMany(
|
|
371
|
-
new BoundQuery(
|
|
372
|
-
`SELECT *, type::string(id) AS id, type::string(organizationId) AS organizationId
|
|
373
|
-
FROM ${TABLES.LEARNED_SKILL}
|
|
374
|
-
WHERE organizationId = $organizationId
|
|
375
|
-
AND hash = $hash
|
|
376
|
-
${ACTIVE_SKILL_FILTER}
|
|
377
|
-
LIMIT 1`,
|
|
378
|
-
{ organizationId: orgRef, hash },
|
|
379
|
-
),
|
|
380
|
-
LearnedSkillRowSchema,
|
|
381
|
-
)
|
|
382
|
-
return rows[0] ?? null
|
|
432
|
+
return {
|
|
433
|
+
create,
|
|
434
|
+
update,
|
|
435
|
+
archive,
|
|
436
|
+
getById,
|
|
437
|
+
searchForTurn,
|
|
438
|
+
retrieveForTurn,
|
|
439
|
+
recordUsage,
|
|
440
|
+
recordSuccess,
|
|
441
|
+
promoteIfEligible,
|
|
442
|
+
findMostSimilar,
|
|
443
|
+
listForOrg,
|
|
444
|
+
generateHash,
|
|
445
|
+
findByNameOrTag,
|
|
446
|
+
findByHash,
|
|
383
447
|
}
|
|
384
448
|
}
|
|
385
449
|
|
|
386
|
-
export
|
|
450
|
+
export class LearnedSkillServiceTag extends Context.Service<
|
|
451
|
+
LearnedSkillServiceTag,
|
|
452
|
+
ReturnType<typeof makeLearnedSkillService>
|
|
453
|
+
>()('LearnedSkillService') {}
|
|
454
|
+
|
|
455
|
+
export const LearnedSkillServiceLive = Layer.effect(
|
|
456
|
+
LearnedSkillServiceTag,
|
|
457
|
+
Effect.gen(function* () {
|
|
458
|
+
const db = yield* DatabaseServiceTag
|
|
459
|
+
const runtimeConfig = yield* RuntimeConfigServiceTag
|
|
460
|
+
const skillExistsCache = yield* Cache.make({
|
|
461
|
+
lookup: (key: string) =>
|
|
462
|
+
Effect.gen(function* () {
|
|
463
|
+
const [orgId, agentId] = key.split(':', 2) as [string, string]
|
|
464
|
+
const orgRef = ensureRecordId(orgId, TABLES.ORGANIZATION)
|
|
465
|
+
const rows = yield* tryLearnedSkillPromise('Failed to check learned skill existence cache.', () =>
|
|
466
|
+
db.query<{ id: unknown }>(
|
|
467
|
+
new BoundQuery(
|
|
468
|
+
`SELECT id FROM ${TABLES.LEARNED_SKILL}
|
|
469
|
+
WHERE organizationId = $orgRef
|
|
470
|
+
${ACTIVE_SKILL_FILTER}
|
|
471
|
+
AND (agentId IS NONE OR agentId = $agentId)
|
|
472
|
+
LIMIT 1`,
|
|
473
|
+
{ orgRef, agentId },
|
|
474
|
+
),
|
|
475
|
+
),
|
|
476
|
+
)
|
|
477
|
+
return rows.length > 0
|
|
478
|
+
}),
|
|
479
|
+
capacity: 256,
|
|
480
|
+
timeToLive: Duration.seconds(SKILL_EXISTS_TTL_SECONDS),
|
|
481
|
+
})
|
|
482
|
+
return makeLearnedSkillService(
|
|
483
|
+
db,
|
|
484
|
+
{
|
|
485
|
+
embeddingModel: runtimeConfig.aiGateway.embeddingModel,
|
|
486
|
+
openRouterApiKey: runtimeConfig.aiGateway.openRouterApiKey,
|
|
487
|
+
},
|
|
488
|
+
skillExistsCache,
|
|
489
|
+
)
|
|
490
|
+
}),
|
|
491
|
+
)
|