@lota-sdk/core 0.4.10 → 0.4.11
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 +2 -2
- package/src/ai-gateway/ai-gateway.ts +149 -95
- package/src/ai-gateway/index.ts +16 -1
- package/src/config/agent-defaults.ts +4 -120
- package/src/config/logger.ts +18 -34
- package/src/config/thread-defaults.ts +1 -18
- package/src/create-runtime.ts +90 -28
- package/src/db/base.service.ts +30 -38
- package/src/db/service.ts +489 -545
- package/src/effect/index.ts +0 -2
- package/src/effect/layers.ts +6 -13
- package/src/embeddings/provider.ts +2 -7
- package/src/index.ts +4 -5
- package/src/queues/autonomous-job.queue.ts +159 -113
- package/src/queues/context-compaction.queue.ts +39 -25
- package/src/queues/delayed-node-promotion.queue.ts +56 -29
- package/src/queues/document-processor.queue.ts +5 -3
- package/src/queues/index.ts +1 -0
- package/src/queues/memory-consolidation.queue.ts +79 -53
- package/src/queues/organization-learning.queue.ts +63 -39
- package/src/queues/plan-agent-heartbeat.queue.ts +104 -79
- package/src/queues/plan-scheduler.queue.ts +100 -84
- package/src/queues/post-chat-memory.queue.ts +55 -33
- package/src/queues/queue-factory.ts +40 -41
- package/src/queues/queues.service.ts +61 -0
- package/src/queues/title-generation.queue.ts +42 -31
- package/src/redis/org-memory-lock.ts +24 -9
- package/src/redis/redis-lease-lock.ts +8 -1
- package/src/runtime/agent-identity-overrides.ts +7 -3
- package/src/runtime/agent-runtime-policy.ts +9 -4
- package/src/runtime/agent-stream-helpers.ts +9 -4
- package/src/runtime/context-compaction/context-compaction-runtime.ts +28 -32
- package/src/runtime/context-compaction/context-compaction.ts +9 -7
- package/src/runtime/domain-layer.ts +15 -4
- package/src/runtime/execution-plan-visibility.ts +5 -2
- package/src/runtime/graph-designer.ts +0 -22
- package/src/runtime/index.ts +1 -0
- package/src/runtime/indexed-repositories-policy.ts +2 -6
- package/src/runtime/plugin-resolution.ts +29 -12
- package/src/runtime/post-turn-side-effects.ts +139 -141
- package/src/runtime/runtime-config.ts +0 -6
- package/src/runtime/runtime-extensions.ts +0 -54
- package/src/runtime/runtime-lifecycle.ts +4 -4
- package/src/runtime/runtime-services.ts +122 -53
- package/src/runtime/runtime-worker-registry.ts +113 -30
- package/src/runtime/social-chat/social-chat-agent-runner.ts +6 -3
- package/src/runtime/social-chat/social-chat-history.ts +3 -1
- package/src/runtime/social-chat/social-chat.ts +35 -20
- package/src/runtime/team-consultation/team-consultation-orchestrator.ts +6 -5
- package/src/runtime/team-consultation/team-consultation-prompts.ts +11 -6
- package/src/runtime/thread-chat-helpers.ts +18 -9
- package/src/runtime/thread-turn-context.ts +7 -47
- package/src/runtime/turn-lifecycle.ts +6 -14
- package/src/services/agent-activity.service.ts +168 -175
- package/src/services/agent-executor.service.ts +35 -16
- package/src/services/attachment.service.ts +4 -70
- package/src/services/autonomous-job.service.ts +53 -61
- package/src/services/context-compaction.service.ts +7 -9
- package/src/services/execution-plan/execution-plan-graph.ts +106 -115
- package/src/services/execution-plan/execution-plan-schedule.ts +1 -15
- package/src/services/execution-plan/execution-plan.service.ts +67 -50
- package/src/services/global-orchestrator.service.ts +18 -7
- package/src/services/graph-full-routing.ts +7 -6
- package/src/services/memory/memory-conversation.ts +10 -5
- package/src/services/memory/memory.service.ts +11 -8
- package/src/services/ownership-dispatcher.service.ts +16 -5
- package/src/services/plan/plan-agent-heartbeat.service.ts +29 -15
- package/src/services/plan/plan-agent-query.service.ts +12 -8
- package/src/services/plan/plan-completion-side-effects.ts +93 -101
- package/src/services/plan/plan-cycle.service.ts +7 -45
- package/src/services/plan/plan-deadline.service.ts +28 -17
- package/src/services/plan/plan-event-delivery.service.ts +47 -40
- package/src/services/plan/plan-executor-context.ts +2 -0
- package/src/services/plan/plan-executor-graph.ts +366 -391
- package/src/services/plan/plan-executor.service.ts +13 -91
- package/src/services/plan/plan-scheduler.service.ts +62 -49
- package/src/services/plan/plan-transaction-events.ts +1 -1
- package/src/services/recent-activity-title.service.ts +6 -2
- package/src/services/thread/thread-bootstrap.ts +11 -9
- package/src/services/thread/thread-message.service.ts +6 -5
- package/src/services/thread/thread-turn-execution.ts +86 -82
- package/src/services/thread/thread-turn-preparation.service.ts +47 -24
- package/src/services/thread/thread-turn-streaming.ts +20 -25
- package/src/services/thread/thread-turn.ts +25 -44
- package/src/services/thread/thread.service.ts +21 -6
- package/src/system-agents/recent-activity-title-refiner.agent.ts +8 -5
- package/src/system-agents/thread-router.agent.ts +23 -20
- package/src/tools/execution-plan.tool.ts +8 -3
- package/src/tools/fetch-webpage.tool.ts +10 -9
- package/src/tools/firecrawl-client.ts +0 -15
- package/src/tools/remember-memory.tool.ts +3 -6
- package/src/tools/research-topic.tool.ts +12 -3
- package/src/tools/search-web.tool.ts +10 -9
- package/src/tools/search.tool.ts +4 -5
- package/src/tools/team-think.tool.ts +139 -121
- package/src/workers/bootstrap.ts +9 -10
- package/src/workers/memory-consolidation.worker.ts +4 -1
- package/src/workers/organization-learning.worker.ts +15 -2
- package/src/workers/regular-chat-memory-digest.helpers.ts +3 -4
- package/src/workers/regular-chat-memory-digest.runner.ts +21 -14
- package/src/workers/skill-extraction.runner.ts +13 -15
- package/src/workers/worker-utils.ts +6 -18
- package/src/effect/awaitable-effect.ts +0 -96
- package/src/effect/runtime-ref.ts +0 -25
- package/src/effect/runtime.ts +0 -46
- package/src/redis/runtime-connection.ts +0 -20
- package/src/runtime/runtime-accessors.ts +0 -92
- package/src/runtime/runtime-token.ts +0 -47
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lota-sdk/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@ai-sdk/openai": "^3.0.53",
|
|
32
32
|
"@chat-adapter/slack": "^4.26.0",
|
|
33
33
|
"@chat-adapter/state-ioredis": "^4.26.0",
|
|
34
|
-
"@lota-sdk/shared": "0.4.
|
|
34
|
+
"@lota-sdk/shared": "0.4.11",
|
|
35
35
|
"@mendable/firecrawl-js": "^4.18.3",
|
|
36
36
|
"@surrealdb/node": "^3.0.3",
|
|
37
37
|
"ai": "^6.0.167",
|
|
@@ -6,7 +6,6 @@ import { Cause, Clock, Context, Duration, Effect, ExecutionPlan, Fiber, Layer, S
|
|
|
6
6
|
|
|
7
7
|
import { DEFAULT_AI_GATEWAY_URL } from '../config/constants'
|
|
8
8
|
import { AiGenerationError, ConfigurationError } from '../effect/errors'
|
|
9
|
-
import { resolveLotaService } from '../effect/runtime'
|
|
10
9
|
import { RuntimeConfigServiceTag } from '../effect/services'
|
|
11
10
|
import { getDirectOpenRouterProvider, normalizeDirectOpenRouterModelId } from '../openrouter/direct-provider'
|
|
12
11
|
import { isRecord, readString } from '../utils/string'
|
|
@@ -24,6 +23,8 @@ type AiGatewayGeneratedContent = AiGatewayGenerateResult['content'][number]
|
|
|
24
23
|
type AiGatewayStreamPart = AiGatewayStreamResult['stream'] extends ReadableStream<infer T> ? T : never
|
|
25
24
|
type AiGatewayProviderOptions = NonNullable<AiGatewayCallOptions['providerOptions']>
|
|
26
25
|
type AiGatewayAttemptResult<A> = { source: string; result: A }
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
|
|
27
|
+
type AiGatewayRunFork = <A, E>(effect: Effect.Effect<A, E, never>) => Fiber.Fiber<A, E | unknown>
|
|
27
28
|
|
|
28
29
|
class AiGatewayGenerateAttempt extends Context.Service<
|
|
29
30
|
AiGatewayGenerateAttempt,
|
|
@@ -271,12 +272,13 @@ function withAiGatewayResilience<A>(source: string, effect: Effect.Effect<A, AiG
|
|
|
271
272
|
function withAiGatewayStreamIdleTimeout(
|
|
272
273
|
stream: ReadableStream<AiGatewayStreamPart>,
|
|
273
274
|
source: string,
|
|
275
|
+
runFork: AiGatewayRunFork,
|
|
274
276
|
onFinalize?: () => void,
|
|
275
277
|
): ReadableStream<AiGatewayStreamPart> {
|
|
276
278
|
let closed = false
|
|
277
279
|
let reader: ReadableStreamDefaultReader<AiGatewayStreamPart> | null = null
|
|
278
|
-
let idleTimeoutFiber:
|
|
279
|
-
let bodyPumpFiber:
|
|
280
|
+
let idleTimeoutFiber: Fiber.Fiber<unknown, unknown> | null = null
|
|
281
|
+
let bodyPumpFiber: Fiber.Fiber<unknown, unknown> | null = null
|
|
280
282
|
let finalized = false
|
|
281
283
|
|
|
282
284
|
const finalize = () => {
|
|
@@ -285,9 +287,9 @@ function withAiGatewayStreamIdleTimeout(
|
|
|
285
287
|
onFinalize?.()
|
|
286
288
|
}
|
|
287
289
|
|
|
288
|
-
const interruptFiber = (fiber:
|
|
290
|
+
const interruptFiber = (fiber: Fiber.Fiber<unknown, unknown> | null) => {
|
|
289
291
|
if (!fiber) return
|
|
290
|
-
void
|
|
292
|
+
void runFork(Fiber.interrupt(fiber))
|
|
291
293
|
}
|
|
292
294
|
|
|
293
295
|
const stopIdleTimeout = () => {
|
|
@@ -351,7 +353,7 @@ function withAiGatewayStreamIdleTimeout(
|
|
|
351
353
|
|
|
352
354
|
const resetIdleTimeout = (controller: ReadableStreamDefaultController<AiGatewayStreamPart>) => {
|
|
353
355
|
stopIdleTimeout()
|
|
354
|
-
idleTimeoutFiber =
|
|
356
|
+
idleTimeoutFiber = runFork(
|
|
355
357
|
Effect.sleep(Duration.millis(AI_GATEWAY_STREAM_IDLE_TIMEOUT_MS)).pipe(
|
|
356
358
|
Effect.flatMap(() =>
|
|
357
359
|
Effect.gen(function* () {
|
|
@@ -417,7 +419,7 @@ function withAiGatewayStreamIdleTimeout(
|
|
|
417
419
|
start(controller) {
|
|
418
420
|
const streamReader = stream.getReader()
|
|
419
421
|
reader = streamReader
|
|
420
|
-
bodyPumpFiber =
|
|
422
|
+
bodyPumpFiber = runFork(pumpStreamEffect(streamReader, controller))
|
|
421
423
|
},
|
|
422
424
|
cancel(reason) {
|
|
423
425
|
closed = true
|
|
@@ -493,40 +495,22 @@ export const AiGatewayLive = Layer.effect(
|
|
|
493
495
|
|
|
494
496
|
type AiGatewayRuntimeConfig = Context.Service.Shape<typeof RuntimeConfigServiceTag>
|
|
495
497
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
})
|
|
503
|
-
currentAiGateway = params.aiGateway
|
|
504
|
-
currentAiGatewayRuntimeConfig = params.runtimeConfig
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
export function clearAiGatewayRuntimeAccessors(): void {
|
|
508
|
-
currentAiGateway = null
|
|
509
|
-
currentAiGatewayRuntimeConfig = null
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
function getAiGateway(): AiGatewayTag['Service'] {
|
|
513
|
-
return currentAiGateway ?? resolveLotaService(AiGatewayTag)
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
function getAiGatewayRuntimeConfig(): AiGatewayRuntimeConfig {
|
|
517
|
-
return currentAiGatewayRuntimeConfig ?? resolveLotaService(RuntimeConfigServiceTag)
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
function withAiGatewayConcurrency<A>(effect: Effect.Effect<A, AiGenerationError>): Effect.Effect<A, AiGenerationError> {
|
|
521
|
-
return getAiGateway().semaphore.withPermit(effect)
|
|
498
|
+
function withAiGatewayConcurrency<A>(
|
|
499
|
+
effect: Effect.Effect<A, AiGenerationError>,
|
|
500
|
+
): Effect.Effect<A, AiGenerationError, AiGatewayTag> {
|
|
501
|
+
return Effect.gen(function* () {
|
|
502
|
+
const gateway = yield* AiGatewayTag
|
|
503
|
+
return yield* gateway.semaphore.withPermit(effect)
|
|
504
|
+
})
|
|
522
505
|
}
|
|
523
506
|
|
|
524
507
|
function withAiGatewayStreamConcurrency(
|
|
525
508
|
effect: Effect.Effect<AiGatewayAttemptResult<AiGatewayStreamResult>, AiGenerationError>,
|
|
526
|
-
|
|
509
|
+
runFork: AiGatewayRunFork,
|
|
510
|
+
): Effect.Effect<AiGatewayAttemptResult<AiGatewayStreamResult>, AiGenerationError, AiGatewayTag> {
|
|
527
511
|
return Effect.uninterruptibleMask((restore) =>
|
|
528
512
|
Effect.gen(function* () {
|
|
529
|
-
const { semaphore } =
|
|
513
|
+
const { semaphore } = yield* AiGatewayTag
|
|
530
514
|
const currentContext = yield* Effect.context<never>()
|
|
531
515
|
yield* semaphore.take(1)
|
|
532
516
|
|
|
@@ -551,7 +535,7 @@ function withAiGatewayStreamConcurrency(
|
|
|
551
535
|
...attempt,
|
|
552
536
|
result: {
|
|
553
537
|
...attempt.result,
|
|
554
|
-
stream: withAiGatewayStreamIdleTimeout(attempt.result.stream, attempt.source, release),
|
|
538
|
+
stream: withAiGatewayStreamIdleTimeout(attempt.result.stream, attempt.source, runFork, release),
|
|
555
539
|
},
|
|
556
540
|
}
|
|
557
541
|
}),
|
|
@@ -638,18 +622,20 @@ function isOpenRouterModel(modelId: string): boolean {
|
|
|
638
622
|
return modelId.trim().toLowerCase().startsWith('openrouter/')
|
|
639
623
|
}
|
|
640
624
|
|
|
641
|
-
function hasDirectOpenRouterFallback(modelId: string): boolean {
|
|
642
|
-
const config = getAiGatewayRuntimeConfig()
|
|
625
|
+
function hasDirectOpenRouterFallback(config: AiGatewayRuntimeConfig, modelId: string): boolean {
|
|
643
626
|
return isOpenRouterModel(modelId) && Boolean(config.aiGateway.openRouterApiKey?.trim())
|
|
644
627
|
}
|
|
645
628
|
|
|
646
|
-
function getDirectOpenRouterChatModel(modelId: string): AiGatewayLanguageModel {
|
|
647
|
-
const config = getAiGatewayRuntimeConfig()
|
|
629
|
+
function getDirectOpenRouterChatModel(config: AiGatewayRuntimeConfig, modelId: string): AiGatewayLanguageModel {
|
|
648
630
|
return getDirectOpenRouterProvider(config.aiGateway.openRouterApiKey).chat(normalizeDirectOpenRouterModelId(modelId))
|
|
649
631
|
}
|
|
650
632
|
|
|
651
|
-
function shouldFallbackToDirectOpenRouter(
|
|
652
|
-
|
|
633
|
+
function shouldFallbackToDirectOpenRouter(
|
|
634
|
+
config: AiGatewayRuntimeConfig,
|
|
635
|
+
modelId: string,
|
|
636
|
+
error: AiGenerationError,
|
|
637
|
+
): boolean {
|
|
638
|
+
return hasDirectOpenRouterFallback(config, modelId) && isRetryableAiGatewayError(error)
|
|
653
639
|
}
|
|
654
640
|
|
|
655
641
|
function attemptAiGatewayGenerate(
|
|
@@ -681,22 +667,25 @@ function attemptAiGatewayStream(
|
|
|
681
667
|
}
|
|
682
668
|
|
|
683
669
|
function attemptDirectOpenRouterGenerate(
|
|
670
|
+
config: AiGatewayRuntimeConfig,
|
|
684
671
|
modelId: string,
|
|
685
672
|
params: AiGatewayCallOptions,
|
|
686
673
|
): Effect.Effect<AiGatewayAttemptResult<AiGatewayGenerateResult>, AiGenerationError> {
|
|
687
|
-
const model = getDirectOpenRouterChatModel(modelId)
|
|
674
|
+
const model = getDirectOpenRouterChatModel(config, modelId)
|
|
688
675
|
return attemptAiGatewayGenerate('openrouter.generate', () => model.doGenerate(params))
|
|
689
676
|
}
|
|
690
677
|
|
|
691
678
|
function attemptDirectOpenRouterStream(
|
|
679
|
+
config: AiGatewayRuntimeConfig,
|
|
692
680
|
modelId: string,
|
|
693
681
|
params: AiGatewayCallOptions,
|
|
694
682
|
): Effect.Effect<AiGatewayAttemptResult<AiGatewayStreamResult>, AiGenerationError> {
|
|
695
|
-
const model = getDirectOpenRouterChatModel(modelId)
|
|
683
|
+
const model = getDirectOpenRouterChatModel(config, modelId)
|
|
696
684
|
return attemptAiGatewayStream('openrouter.stream', () => model.doStream(params))
|
|
697
685
|
}
|
|
698
686
|
|
|
699
687
|
function executeGenerateAttemptPlan(
|
|
688
|
+
config: AiGatewayRuntimeConfig,
|
|
700
689
|
modelId: string,
|
|
701
690
|
params: AiGatewayCallOptions,
|
|
702
691
|
doGenerate: () => PromiseLike<AiGatewayGenerateResult>,
|
|
@@ -709,7 +698,7 @@ function executeGenerateAttemptPlan(
|
|
|
709
698
|
return yield* attempt.execute
|
|
710
699
|
})
|
|
711
700
|
|
|
712
|
-
if (!hasDirectOpenRouterFallback(modelId)) {
|
|
701
|
+
if (!hasDirectOpenRouterFallback(config, modelId)) {
|
|
713
702
|
return effect.pipe(
|
|
714
703
|
Effect.provide(primary),
|
|
715
704
|
Effect.withSpan('AiGateway.executeGeneratePlan'),
|
|
@@ -723,9 +712,9 @@ function executeGenerateAttemptPlan(
|
|
|
723
712
|
{ provide: primary },
|
|
724
713
|
{
|
|
725
714
|
provide: Layer.succeed(AiGatewayGenerateAttempt, {
|
|
726
|
-
execute: attemptDirectOpenRouterGenerate(modelId, params),
|
|
715
|
+
execute: attemptDirectOpenRouterGenerate(config, modelId, params),
|
|
727
716
|
}),
|
|
728
|
-
while: (error: AiGenerationError) => shouldFallbackToDirectOpenRouter(modelId, error),
|
|
717
|
+
while: (error: AiGenerationError) => shouldFallbackToDirectOpenRouter(config, modelId, error),
|
|
729
718
|
},
|
|
730
719
|
),
|
|
731
720
|
),
|
|
@@ -735,6 +724,7 @@ function executeGenerateAttemptPlan(
|
|
|
735
724
|
}
|
|
736
725
|
|
|
737
726
|
function executeStreamAttemptPlan(
|
|
727
|
+
config: AiGatewayRuntimeConfig,
|
|
738
728
|
modelId: string,
|
|
739
729
|
params: AiGatewayCallOptions,
|
|
740
730
|
doStream: () => PromiseLike<AiGatewayStreamResult>,
|
|
@@ -747,7 +737,7 @@ function executeStreamAttemptPlan(
|
|
|
747
737
|
return yield* attempt.execute
|
|
748
738
|
})
|
|
749
739
|
|
|
750
|
-
if (!hasDirectOpenRouterFallback(modelId)) {
|
|
740
|
+
if (!hasDirectOpenRouterFallback(config, modelId)) {
|
|
751
741
|
return effect.pipe(
|
|
752
742
|
Effect.provide(primary),
|
|
753
743
|
Effect.withSpan('AiGateway.executeStreamPlan'),
|
|
@@ -760,8 +750,10 @@ function executeStreamAttemptPlan(
|
|
|
760
750
|
ExecutionPlan.make(
|
|
761
751
|
{ provide: primary },
|
|
762
752
|
{
|
|
763
|
-
provide: Layer.succeed(AiGatewayStreamAttempt, {
|
|
764
|
-
|
|
753
|
+
provide: Layer.succeed(AiGatewayStreamAttempt, {
|
|
754
|
+
execute: attemptDirectOpenRouterStream(config, modelId, params),
|
|
755
|
+
}),
|
|
756
|
+
while: (error: AiGenerationError) => shouldFallbackToDirectOpenRouter(config, modelId, error),
|
|
765
757
|
},
|
|
766
758
|
),
|
|
767
759
|
),
|
|
@@ -849,7 +841,56 @@ function addAiGatewayReasoningRawChunks(
|
|
|
849
841
|
return { ...params, includeRawChunks: true }
|
|
850
842
|
}
|
|
851
843
|
|
|
852
|
-
function
|
|
844
|
+
function resolveProviderModel(
|
|
845
|
+
provider: ReturnType<typeof createOpenAI>,
|
|
846
|
+
modelId: string,
|
|
847
|
+
providerId: string,
|
|
848
|
+
): AiGatewayLanguageModel {
|
|
849
|
+
return providerId === OPENAI_CHAT_PROVIDER_ID ? provider.chat(modelId) : provider(modelId)
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// Module-level Promise slot that `createLotaRuntime` populates during boot.
|
|
853
|
+
// This is a legitimate per-process singleton (mirrors the worker bootstrap
|
|
854
|
+
// pattern in `workers/bootstrap.ts`): the AI gateway middleware is dispatched
|
|
855
|
+
// by AI SDK callers that live outside Effect context, so the middleware needs
|
|
856
|
+
// a way to run gateway Effects without capturing a `ManagedRuntime` through
|
|
857
|
+
// every `aiGatewayModel(modelId)` call site.
|
|
858
|
+
//
|
|
859
|
+
// Only `createLotaRuntime` writes to the slot; resetting on disconnect is a
|
|
860
|
+
// Phase 3b concern — for now it stays alive for the process lifetime.
|
|
861
|
+
let aiGatewayRuntimeReady: Promise<{
|
|
862
|
+
gateway: Context.Service.Shape<typeof AiGatewayTag>
|
|
863
|
+
runtimeConfig: Context.Service.Shape<typeof RuntimeConfigServiceTag>
|
|
864
|
+
runPromise: <A, E>(effect: Effect.Effect<A, E, never>) => Promise<A>
|
|
865
|
+
runFork: AiGatewayRunFork
|
|
866
|
+
}> | null = null
|
|
867
|
+
|
|
868
|
+
export function bindAiGatewayRuntime(params: {
|
|
869
|
+
gateway: Context.Service.Shape<typeof AiGatewayTag>
|
|
870
|
+
runtimeConfig: Context.Service.Shape<typeof RuntimeConfigServiceTag>
|
|
871
|
+
runPromise: <A, E>(effect: Effect.Effect<A, E, never>) => Promise<A>
|
|
872
|
+
runFork: AiGatewayRunFork
|
|
873
|
+
}): void {
|
|
874
|
+
aiGatewayRuntimeReady = Promise.resolve(params)
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
export function clearAiGatewayRuntime(): void {
|
|
878
|
+
aiGatewayRuntimeReady = null
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
async function getAiGatewayRuntime(): Promise<{
|
|
882
|
+
gateway: Context.Service.Shape<typeof AiGatewayTag>
|
|
883
|
+
runtimeConfig: Context.Service.Shape<typeof RuntimeConfigServiceTag>
|
|
884
|
+
runPromise: <A, E>(effect: Effect.Effect<A, E, never>) => Promise<A>
|
|
885
|
+
runFork: AiGatewayRunFork
|
|
886
|
+
}> {
|
|
887
|
+
if (!aiGatewayRuntimeReady) {
|
|
888
|
+
throw new Error('AI gateway runtime has not been initialized. Call createLotaRuntime() first.')
|
|
889
|
+
}
|
|
890
|
+
return aiGatewayRuntimeReady
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function createAiGatewayLanguageModelMiddleware(modelId: string, providerId: string): LanguageModelMiddleware {
|
|
853
894
|
return {
|
|
854
895
|
specificationVersion: 'v3',
|
|
855
896
|
transformParams: ({ params, type }) =>
|
|
@@ -858,10 +899,12 @@ function createAiGatewayLanguageModelMiddleware(modelId: string): LanguageModelM
|
|
|
858
899
|
addAiGatewayReasoningRawChunks(normalizeAiGatewayChatProviderOptions(params, modelId), type),
|
|
859
900
|
),
|
|
860
901
|
),
|
|
861
|
-
wrapGenerate: ({
|
|
862
|
-
|
|
902
|
+
wrapGenerate: async ({ params }) => {
|
|
903
|
+
const { gateway, runtimeConfig, runPromise } = await getAiGatewayRuntime()
|
|
904
|
+
const model = resolveProviderModel(gateway.provider, modelId, providerId)
|
|
905
|
+
return runPromise(
|
|
863
906
|
withAiGatewayConcurrency(
|
|
864
|
-
executeGenerateAttemptPlan(modelId, params, doGenerate).pipe(
|
|
907
|
+
executeGenerateAttemptPlan(runtimeConfig, modelId, params, () => model.doGenerate(params)).pipe(
|
|
865
908
|
Effect.map(({ result }) => ({
|
|
866
909
|
...result,
|
|
867
910
|
content: injectAiGatewayChatReasoningContent(
|
|
@@ -870,12 +913,15 @@ function createAiGatewayLanguageModelMiddleware(modelId: string): LanguageModelM
|
|
|
870
913
|
),
|
|
871
914
|
})),
|
|
872
915
|
),
|
|
873
|
-
),
|
|
874
|
-
)
|
|
875
|
-
|
|
876
|
-
|
|
916
|
+
).pipe(Effect.provideService(AiGatewayTag, gateway)),
|
|
917
|
+
)
|
|
918
|
+
},
|
|
919
|
+
wrapStream: async ({ params }) => {
|
|
920
|
+
const { gateway, runtimeConfig, runPromise, runFork } = await getAiGatewayRuntime()
|
|
921
|
+
const model = resolveProviderModel(gateway.provider, modelId, providerId)
|
|
922
|
+
return runPromise(
|
|
877
923
|
withAiGatewayStreamConcurrency(
|
|
878
|
-
executeStreamAttemptPlan(modelId, params, doStream).pipe(
|
|
924
|
+
executeStreamAttemptPlan(runtimeConfig, modelId, params, () => model.doStream(params)).pipe(
|
|
879
925
|
Effect.map((attempt) => ({
|
|
880
926
|
...attempt,
|
|
881
927
|
result: isReasoningEnabled(params)
|
|
@@ -883,8 +929,12 @@ function createAiGatewayLanguageModelMiddleware(modelId: string): LanguageModelM
|
|
|
883
929
|
: attempt.result,
|
|
884
930
|
})),
|
|
885
931
|
),
|
|
886
|
-
|
|
887
|
-
|
|
932
|
+
runFork,
|
|
933
|
+
)
|
|
934
|
+
.pipe(Effect.map(({ result }) => result))
|
|
935
|
+
.pipe(Effect.provideService(AiGatewayTag, gateway)),
|
|
936
|
+
)
|
|
937
|
+
},
|
|
888
938
|
}
|
|
889
939
|
}
|
|
890
940
|
|
|
@@ -921,36 +971,42 @@ function withAiGatewayDevTools<TModel extends AiGatewayLanguageModel>(model: TMo
|
|
|
921
971
|
return wrapLanguageModel({ model, middleware: devToolsMiddleware() }) as TModel
|
|
922
972
|
}
|
|
923
973
|
|
|
924
|
-
function
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
974
|
+
function createAiGatewayLanguageModelPlaceholder(modelId: string, providerId: string): AiGatewayLanguageModel {
|
|
975
|
+
const unreachable = (method: string) =>
|
|
976
|
+
Promise.reject(
|
|
977
|
+
new Error(
|
|
978
|
+
`[ai-gateway] AiGateway language model ${modelId}.${method} was invoked without the gateway middleware; ` +
|
|
979
|
+
'this call path should be fully handled by createAiGatewayLanguageModelMiddleware.',
|
|
980
|
+
),
|
|
981
|
+
)
|
|
982
|
+
|
|
929
983
|
return {
|
|
930
984
|
specificationVersion: 'v3',
|
|
931
|
-
provider:
|
|
932
|
-
modelId
|
|
985
|
+
provider: providerId,
|
|
986
|
+
modelId,
|
|
933
987
|
supportedUrls: {},
|
|
934
|
-
doGenerate: (
|
|
935
|
-
doStream: (
|
|
988
|
+
doGenerate: () => unreachable('doGenerate'),
|
|
989
|
+
doStream: () => unreachable('doStream'),
|
|
936
990
|
}
|
|
937
991
|
}
|
|
938
992
|
|
|
939
|
-
function
|
|
993
|
+
function createAiGatewayEmbeddingModelPlaceholder(modelId: string): AiGatewayEmbeddingModel {
|
|
940
994
|
return {
|
|
941
995
|
specificationVersion: 'v3',
|
|
942
996
|
provider: OPENAI_EMBEDDING_PROVIDER_ID,
|
|
943
997
|
modelId,
|
|
944
998
|
maxEmbeddingsPerCall: OPENAI_EMBEDDING_MAX_PER_CALL,
|
|
945
999
|
supportsParallelCalls: true,
|
|
946
|
-
doEmbed: (
|
|
1000
|
+
doEmbed: () =>
|
|
1001
|
+
Promise.reject(
|
|
1002
|
+
new Error(
|
|
1003
|
+
`[ai-gateway] AiGateway embedding model ${modelId}.doEmbed was invoked without the gateway middleware; ` +
|
|
1004
|
+
'this call path should be fully handled by aiGatewayEmbeddingModel middleware.',
|
|
1005
|
+
),
|
|
1006
|
+
),
|
|
947
1007
|
}
|
|
948
1008
|
}
|
|
949
1009
|
|
|
950
|
-
export function getAiGatewayProvider() {
|
|
951
|
-
return getAiGateway().provider
|
|
952
|
-
}
|
|
953
|
-
|
|
954
1010
|
export function aiGatewayModel(modelId: string) {
|
|
955
1011
|
if (isOpenRouterModel(modelId)) {
|
|
956
1012
|
return aiGatewayChatModel(modelId)
|
|
@@ -958,12 +1014,8 @@ export function aiGatewayModel(modelId: string) {
|
|
|
958
1014
|
|
|
959
1015
|
return withAiGatewayDevTools(
|
|
960
1016
|
wrapLanguageModel({
|
|
961
|
-
model:
|
|
962
|
-
|
|
963
|
-
providerId: OPENAI_RESPONSES_PROVIDER_ID,
|
|
964
|
-
resolve: () => getAiGatewayProvider()(modelId),
|
|
965
|
-
}),
|
|
966
|
-
middleware: createAiGatewayLanguageModelMiddleware(modelId),
|
|
1017
|
+
model: createAiGatewayLanguageModelPlaceholder(modelId, OPENAI_RESPONSES_PROVIDER_ID),
|
|
1018
|
+
middleware: createAiGatewayLanguageModelMiddleware(modelId, OPENAI_RESPONSES_PROVIDER_ID),
|
|
967
1019
|
}),
|
|
968
1020
|
)
|
|
969
1021
|
}
|
|
@@ -975,30 +1027,32 @@ export function aiGatewayOpenRouterResponseHealingModel(modelId: string) {
|
|
|
975
1027
|
export function aiGatewayChatModel(modelId: string) {
|
|
976
1028
|
return withAiGatewayDevTools(
|
|
977
1029
|
wrapLanguageModel({
|
|
978
|
-
model:
|
|
979
|
-
|
|
980
|
-
providerId: OPENAI_CHAT_PROVIDER_ID,
|
|
981
|
-
resolve: () => getAiGatewayProvider().chat(modelId),
|
|
982
|
-
}),
|
|
983
|
-
middleware: createAiGatewayLanguageModelMiddleware(modelId),
|
|
1030
|
+
model: createAiGatewayLanguageModelPlaceholder(modelId, OPENAI_CHAT_PROVIDER_ID),
|
|
1031
|
+
middleware: createAiGatewayLanguageModelMiddleware(modelId, OPENAI_CHAT_PROVIDER_ID),
|
|
984
1032
|
}),
|
|
985
1033
|
)
|
|
986
1034
|
}
|
|
987
1035
|
|
|
988
1036
|
export function aiGatewayEmbeddingModel(modelId: string) {
|
|
989
1037
|
return wrapEmbeddingModel({
|
|
990
|
-
model:
|
|
1038
|
+
model: createAiGatewayEmbeddingModelPlaceholder(modelId),
|
|
991
1039
|
middleware: {
|
|
992
1040
|
specificationVersion: 'v3',
|
|
993
|
-
wrapEmbed: ({
|
|
994
|
-
|
|
1041
|
+
wrapEmbed: async ({ params }) => {
|
|
1042
|
+
const { gateway, runPromise } = await getAiGatewayRuntime()
|
|
1043
|
+
const embeddingModel = gateway.provider.embeddingModel(modelId)
|
|
1044
|
+
return runPromise(
|
|
995
1045
|
withAiGatewayConcurrency(
|
|
996
1046
|
withAiGatewayResilience(
|
|
997
1047
|
'ai-gateway.embed',
|
|
998
|
-
Effect.tryPromise({
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1048
|
+
Effect.tryPromise({
|
|
1049
|
+
try: () => embeddingModel.doEmbed(params),
|
|
1050
|
+
catch: (cause) => classifyAiGatewayError('ai-gateway.embed', cause),
|
|
1051
|
+
}),
|
|
1052
|
+
).pipe(Effect.withSpan('AiGateway.embed'), Effect.annotateSpans({ modelId })),
|
|
1053
|
+
).pipe(Effect.provideService(AiGatewayTag, gateway)),
|
|
1054
|
+
)
|
|
1055
|
+
},
|
|
1002
1056
|
},
|
|
1003
1057
|
})
|
|
1004
1058
|
}
|
package/src/ai-gateway/index.ts
CHANGED
|
@@ -1,2 +1,17 @@
|
|
|
1
|
-
export
|
|
1
|
+
export {
|
|
2
|
+
AiGatewayLive,
|
|
3
|
+
AiGatewayTag,
|
|
4
|
+
DEFAULT_AI_GATEWAY_URL,
|
|
5
|
+
aiGatewayChatModel,
|
|
6
|
+
aiGatewayEmbeddingModel,
|
|
7
|
+
aiGatewayModel,
|
|
8
|
+
aiGatewayOpenRouterResponseHealingModel,
|
|
9
|
+
bindAiGatewayRuntime,
|
|
10
|
+
extractAiGatewayChatReasoningDeltaText,
|
|
11
|
+
extractAiGatewayChatReasoningText,
|
|
12
|
+
injectAiGatewayChatReasoningContent,
|
|
13
|
+
injectAiGatewayChatReasoningStream,
|
|
14
|
+
normalizeAiGatewayChatProviderOptions,
|
|
15
|
+
normalizeAiGatewayUrl,
|
|
16
|
+
} from './ai-gateway'
|
|
2
17
|
export * from './cache-headers'
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { ToolSet } from 'ai'
|
|
2
2
|
|
|
3
3
|
import { ConfigurationError } from '../effect/errors'
|
|
4
|
-
import { resolveLotaService } from '../effect/runtime'
|
|
5
|
-
import { AgentConfigServiceTag, AgentFactoryServiceTag } from '../effect/services'
|
|
6
4
|
import type {
|
|
7
5
|
AgentFactory,
|
|
8
6
|
AgentRuntimeConfigParams,
|
|
@@ -130,125 +128,11 @@ export interface CoreThreadProfile {
|
|
|
130
128
|
instructions: string
|
|
131
129
|
}
|
|
132
130
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
export function configureAgentRuntimeDefaults(params: {
|
|
137
|
-
agentConfig: ResolvedAgentConfig
|
|
138
|
-
agentFactoryConfig: ResolvedAgentFactoryConfig
|
|
139
|
-
}): void {
|
|
140
|
-
currentResolvedAgentConfig = params.agentConfig
|
|
141
|
-
currentResolvedAgentFactoryConfig = params.agentFactoryConfig
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function clearAgentRuntimeDefaults(): void {
|
|
145
|
-
currentResolvedAgentConfig = null
|
|
146
|
-
currentResolvedAgentFactoryConfig = null
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function resolveAgentConfigFromRuntime(): ResolvedAgentConfig {
|
|
150
|
-
return currentResolvedAgentConfig ?? resolveLotaService(AgentConfigServiceTag)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function resolveAgentFactoryConfigFromRuntime(): ResolvedAgentFactoryConfig {
|
|
154
|
-
return currentResolvedAgentFactoryConfig ?? resolveLotaService(AgentFactoryServiceTag)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function getResolvedAgentConfig(): ResolvedAgentConfig {
|
|
158
|
-
return resolveAgentConfigFromRuntime()
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function getResolvedAgentFactoryConfig(): ResolvedAgentFactoryConfig {
|
|
162
|
-
return resolveAgentFactoryConfigFromRuntime()
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export function isAgentName(value: unknown): value is string {
|
|
166
|
-
return typeof value === 'string' && resolveAgentConfigFromRuntime().rosterSet.has(value)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export function getAgentRoster(): readonly string[] {
|
|
170
|
-
return resolveAgentConfigFromRuntime().roster
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export function getAgentDisplayNames(): Record<string, string> {
|
|
174
|
-
return resolveAgentConfigFromRuntime().displayNames
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
export function getAgentShortDisplayNames(): Record<string, string> {
|
|
178
|
-
return resolveAgentConfigFromRuntime().shortDisplayNames
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export function getAgentDescriptions(): Record<string, string> {
|
|
182
|
-
return resolveAgentConfigFromRuntime().descriptions
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export function getLeadAgentId(): string {
|
|
186
|
-
return resolveAgentConfigFromRuntime().leadAgentId
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
export function getLeadAgentDisplayName(): string {
|
|
190
|
-
const resolved = resolveAgentConfigFromRuntime()
|
|
191
|
-
return resolved.displayNames[resolved.leadAgentId] ?? resolved.leadAgentId
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export function getRouterModelId(): string | undefined {
|
|
195
|
-
return resolveAgentConfigFromRuntime().routerModelId
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export function getTeamConsultParticipants(): readonly string[] {
|
|
199
|
-
return resolveAgentConfigFromRuntime().teamConsultParticipants
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export function getCoreThreadProfile(coreType: string): CoreThreadProfile {
|
|
203
|
-
return resolveAgentConfigFromRuntime().getCoreThreadProfile(coreType)
|
|
131
|
+
export function isAgentName(agentConfig: ResolvedAgentConfig, value: unknown): value is string {
|
|
132
|
+
return typeof value === 'string' && agentConfig.rosterSet.has(value)
|
|
204
133
|
}
|
|
205
134
|
|
|
206
|
-
export function resolveAgentNameAlias(value: unknown): string | undefined {
|
|
135
|
+
export function resolveAgentNameAlias(agentConfig: ResolvedAgentConfig, value: unknown): string | undefined {
|
|
207
136
|
if (typeof value !== 'string') return undefined
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export function getCreateAgentRegistry(): AgentFactory {
|
|
212
|
-
return resolveAgentFactoryConfigFromRuntime().createAgent
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export function buildAgentTools(...args: Parameters<AgentToolBuilder>): ReturnType<AgentToolBuilder> {
|
|
216
|
-
return resolveAgentFactoryConfigFromRuntime().buildAgentTools(...args)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
export function getAgentRuntimeConfig(
|
|
220
|
-
...args: Parameters<AgentRuntimeConfigProvider>
|
|
221
|
-
): ReturnType<AgentRuntimeConfigProvider> {
|
|
222
|
-
return resolveAgentFactoryConfigFromRuntime().getAgentRuntimeConfig(...args)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export function getPluginRuntime(): Record<string, unknown> | undefined {
|
|
226
|
-
return resolveAgentFactoryConfigFromRuntime().pluginRuntime
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const AGENT_MENTION_REGEX = /(^|[^\w])@([a-z][a-z0-9_-]*)\b/gi
|
|
230
|
-
|
|
231
|
-
export interface AgentMentionMatch {
|
|
232
|
-
agent: string
|
|
233
|
-
mention: string
|
|
234
|
-
index: number
|
|
235
|
-
length: number
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export function extractAgentMentions(message: string, agentConfig?: ResolvedAgentConfig): AgentMentionMatch[] {
|
|
239
|
-
const matches: AgentMentionMatch[] = []
|
|
240
|
-
if (!message.trim()) return matches
|
|
241
|
-
|
|
242
|
-
const resolvedConfig = agentConfig ?? resolveAgentConfigFromRuntime()
|
|
243
|
-
const regex = new RegExp(AGENT_MENTION_REGEX)
|
|
244
|
-
for (const rawMatch of message.matchAll(regex)) {
|
|
245
|
-
const prefix = rawMatch[1]
|
|
246
|
-
const rawAgent = rawMatch[2].toLowerCase()
|
|
247
|
-
if (!resolvedConfig.rosterSet.has(rawAgent)) continue
|
|
248
|
-
|
|
249
|
-
const index = rawMatch.index + prefix.length
|
|
250
|
-
matches.push({ agent: rawAgent, mention: `@${rawAgent}`, index, length: rawAgent.length + 1 })
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return matches
|
|
137
|
+
return agentConfig.aliasMap.get(normalizeAgentLookupKey(value))
|
|
254
138
|
}
|