@lota-sdk/core 0.4.22 → 0.4.24
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lota-sdk/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.24",
|
|
4
4
|
"files": [
|
|
5
5
|
"src",
|
|
6
6
|
"infrastructure/schema"
|
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@ai-sdk/devtools": "^0.0.16",
|
|
31
31
|
"@ai-sdk/openai": "^3.0.54",
|
|
32
|
+
"@ai-sdk/provider": "^3.0.9",
|
|
32
33
|
"@chat-adapter/slack": "^4.26.0",
|
|
33
34
|
"@chat-adapter/state-ioredis": "^4.26.0",
|
|
34
|
-
"@lota-sdk/shared": "0.4.
|
|
35
|
+
"@lota-sdk/shared": "0.4.24",
|
|
35
36
|
"@mendable/firecrawl-js": "^4.20.0",
|
|
36
37
|
"@surrealdb/node": "^3.0.3",
|
|
37
38
|
"ai": "^6.0.170",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { devToolsMiddleware } from '@ai-sdk/devtools'
|
|
2
2
|
import { createOpenAI } from '@ai-sdk/openai'
|
|
3
|
+
import type { JSONSchema7 } from '@ai-sdk/provider'
|
|
3
4
|
import { wrapEmbeddingModel, wrapLanguageModel } from 'ai'
|
|
4
5
|
import type { LanguageModelMiddleware } from 'ai'
|
|
5
6
|
import { Cause, Clock, Context, Duration, Effect, Fiber, Layer, Semaphore } from 'effect'
|
|
@@ -16,6 +17,7 @@ type WrapStreamOptions = Parameters<NonNullable<LanguageModelMiddleware['wrapStr
|
|
|
16
17
|
type AiGatewayLanguageModel = Parameters<typeof wrapLanguageModel>[0]['model']
|
|
17
18
|
type AiGatewayEmbeddingModel = Parameters<typeof wrapEmbeddingModel>[0]['model']
|
|
18
19
|
type AiGatewayCallOptions = WrapStreamOptions['params']
|
|
20
|
+
type AiGatewayFunctionTool = Extract<NonNullable<AiGatewayCallOptions['tools']>[number], { type: 'function' }>
|
|
19
21
|
type AiGatewayGenerateResult = Awaited<ReturnType<WrapStreamOptions['doGenerate']>>
|
|
20
22
|
type AiGatewayStreamResult = Awaited<ReturnType<WrapStreamOptions['doStream']>>
|
|
21
23
|
type AiGatewayGeneratedContent = AiGatewayGenerateResult['content'][number]
|
|
@@ -72,6 +74,17 @@ function isAiGenerationError(error: unknown): error is AiGenerationError {
|
|
|
72
74
|
return isRecord(error) && error._tag === ERROR_TAGS.AiGenerationError
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
export function isAiGenerationContentFilterError(error: unknown): error is AiGenerationError {
|
|
78
|
+
if (!isAiGenerationError(error)) return false
|
|
79
|
+
|
|
80
|
+
const text = [error.message, error.providerData, error.responseBody]
|
|
81
|
+
.filter((part): part is string => typeof part === 'string' && part.length > 0)
|
|
82
|
+
.join('\n')
|
|
83
|
+
.toLowerCase()
|
|
84
|
+
|
|
85
|
+
return text.includes('content_filter') || text.includes('content management policy')
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
function getNumericField(value: Record<string, unknown>, key: string): number | null {
|
|
76
89
|
const field = value[key]
|
|
77
90
|
if (typeof field === 'number' && Number.isFinite(field)) return field
|
|
@@ -900,52 +913,55 @@ const JSON_SCHEMA_PROPERTY_MAP_KEYS = new Set([
|
|
|
900
913
|
'properties',
|
|
901
914
|
])
|
|
902
915
|
|
|
903
|
-
function
|
|
916
|
+
function removeJsonSchemaFormatKeywords(value: unknown): void {
|
|
904
917
|
if (Array.isArray(value)) {
|
|
905
|
-
|
|
918
|
+
for (const item of value) {
|
|
919
|
+
removeJsonSchemaFormatKeywords(item)
|
|
920
|
+
}
|
|
921
|
+
return
|
|
906
922
|
}
|
|
907
923
|
|
|
908
924
|
if (!isRecord(value)) {
|
|
909
|
-
return
|
|
925
|
+
return
|
|
910
926
|
}
|
|
911
927
|
|
|
912
|
-
|
|
913
|
-
const nextValue: Record<string, unknown> = {}
|
|
928
|
+
delete value.format
|
|
914
929
|
|
|
915
930
|
for (const [key, child] of Object.entries(value)) {
|
|
916
|
-
if (key === 'format') {
|
|
917
|
-
changed = true
|
|
918
|
-
continue
|
|
919
|
-
}
|
|
920
|
-
|
|
921
931
|
if (JSON_SCHEMA_PROPERTY_MAP_KEYS.has(key) && isRecord(child)) {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
for (const [propertyName, propertySchema] of Object.entries(child)) {
|
|
925
|
-
const nextPropertySchema = stripJsonSchemaFormatKeywords(propertySchema)
|
|
926
|
-
mapChanged ||= nextPropertySchema !== propertySchema
|
|
927
|
-
nextMap[propertyName] = nextPropertySchema
|
|
932
|
+
for (const propertySchema of Object.values(child)) {
|
|
933
|
+
removeJsonSchemaFormatKeywords(propertySchema)
|
|
928
934
|
}
|
|
929
|
-
changed ||= mapChanged
|
|
930
|
-
nextValue[key] = mapChanged ? nextMap : child
|
|
931
935
|
continue
|
|
932
936
|
}
|
|
933
937
|
|
|
934
|
-
|
|
935
|
-
changed ||= nextChild !== child
|
|
936
|
-
nextValue[key] = nextChild
|
|
938
|
+
removeJsonSchemaFormatKeywords(child)
|
|
937
939
|
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
function cloneAiGatewayJsonSchema(schema: JSONSchema7): JSONSchema7 {
|
|
943
|
+
// eslint-disable-next-line typescript-eslint/no-unsafe-assignment -- structuredClone preserves the imported JSONSchema7 shape.
|
|
944
|
+
const nextSchema: JSONSchema7 = structuredClone(schema)
|
|
945
|
+
removeJsonSchemaFormatKeywords(nextSchema)
|
|
946
|
+
return nextSchema
|
|
947
|
+
}
|
|
938
948
|
|
|
939
|
-
|
|
949
|
+
function isAiGatewayFunctionTool(
|
|
950
|
+
tool: NonNullable<AiGatewayCallOptions['tools']>[number],
|
|
951
|
+
): tool is AiGatewayFunctionTool {
|
|
952
|
+
return isRecord(tool) && tool.type === 'function'
|
|
940
953
|
}
|
|
941
954
|
|
|
942
955
|
export function normalizeAiGatewayJsonSchemas(params: AiGatewayCallOptions): AiGatewayCallOptions {
|
|
943
956
|
let nextParams = params
|
|
944
957
|
|
|
945
958
|
if (params.responseFormat?.type === 'json' && params.responseFormat.schema) {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
959
|
+
// eslint-disable-next-line typescript-eslint/no-unsafe-assignment -- cloneAiGatewayJsonSchema returns the same JSONSchema7 type.
|
|
960
|
+
const responseSchema: JSONSchema7 = cloneAiGatewayJsonSchema(params.responseFormat.schema)
|
|
961
|
+
nextParams = {
|
|
962
|
+
...nextParams,
|
|
963
|
+
// eslint-disable-next-line typescript-eslint/no-unsafe-assignment -- responseSchema is JSONSchema7 for responseFormat.schema.
|
|
964
|
+
responseFormat: { ...params.responseFormat, schema: responseSchema },
|
|
949
965
|
}
|
|
950
966
|
}
|
|
951
967
|
|
|
@@ -955,13 +971,14 @@ export function normalizeAiGatewayJsonSchemas(params: AiGatewayCallOptions): AiG
|
|
|
955
971
|
}
|
|
956
972
|
|
|
957
973
|
const nextTools = sourceTools.map((tool) => {
|
|
958
|
-
if (!
|
|
974
|
+
if (!isAiGatewayFunctionTool(tool)) {
|
|
959
975
|
return tool
|
|
960
976
|
}
|
|
961
977
|
|
|
962
|
-
|
|
963
|
-
const
|
|
964
|
-
|
|
978
|
+
// eslint-disable-next-line typescript-eslint/no-unsafe-assignment -- function tool inputSchema is JSONSchema7.
|
|
979
|
+
const inputSchema: JSONSchema7 = cloneAiGatewayJsonSchema(tool.inputSchema)
|
|
980
|
+
// eslint-disable-next-line typescript-eslint/no-unsafe-assignment -- inputSchema is JSONSchema7 for function tool inputSchema.
|
|
981
|
+
return { ...tool, inputSchema }
|
|
965
982
|
})
|
|
966
983
|
|
|
967
984
|
return { ...nextParams, tools: nextTools as AiGatewayCallOptions['tools'] }
|
package/src/ai-gateway/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export {
|
|
|
13
13
|
extractAiGatewayChatReasoningText,
|
|
14
14
|
injectAiGatewayChatReasoningContent,
|
|
15
15
|
injectAiGatewayChatReasoningStream,
|
|
16
|
+
isAiGenerationContentFilterError,
|
|
16
17
|
normalizeAiGatewayChatProviderOptions,
|
|
17
18
|
normalizeAiGatewayJsonSchemas,
|
|
18
19
|
normalizeAiGatewayUrl,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Context, Effect, Layer } from 'effect'
|
|
2
2
|
|
|
3
|
-
import { AiGatewayModelsTag } from '../ai-gateway/ai-gateway'
|
|
3
|
+
import { AiGatewayModelsTag, isAiGenerationContentFilterError } from '../ai-gateway/ai-gateway'
|
|
4
4
|
import type { AiGatewayModels } from '../ai-gateway/ai-gateway'
|
|
5
5
|
import type { ResolvedAgentConfig } from '../config/agent-defaults'
|
|
6
|
-
import {
|
|
6
|
+
import { chatLogger } from '../config/logger'
|
|
7
|
+
import { ERROR_TAGS, ServiceError } from '../effect/errors'
|
|
7
8
|
import { AgentConfigServiceTag } from '../effect/services'
|
|
8
9
|
import type { HelperModelRuntime } from '../runtime/helper-model'
|
|
9
10
|
import { HelperModelTag } from '../runtime/helper-model'
|
|
@@ -62,20 +63,34 @@ export function makeRecentActivityTitleService(
|
|
|
62
63
|
return
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
const maybeRefinedTitle = yield* Effect.tryPromise({
|
|
67
|
+
try: () =>
|
|
68
|
+
helperModelRuntime.generateHelperText({
|
|
69
|
+
tag: 'recent-activity-title-refinement',
|
|
70
|
+
createAgent: refinerAgentFactory,
|
|
71
|
+
defaultSystemPrompt: RECENT_ACTIVITY_TITLE_REFINER_PROMPT,
|
|
72
|
+
timeoutMs: RECENT_ACTIVITY_TITLE_TIMEOUT_MS,
|
|
73
|
+
messages: [{ role: 'user', content: promptInput }],
|
|
74
|
+
}),
|
|
75
|
+
catch: (cause) =>
|
|
76
|
+
isAiGenerationContentFilterError(cause)
|
|
77
|
+
? cause
|
|
78
|
+
: new ServiceError({ message: 'Failed to generate recent activity title refinement.', cause }),
|
|
79
|
+
}).pipe(
|
|
80
|
+
Effect.catchTag(ERROR_TAGS.AiGenerationError, (error) =>
|
|
81
|
+
isAiGenerationContentFilterError(error)
|
|
82
|
+
? Effect.sync(() => {
|
|
83
|
+
chatLogger.warn`Skipping recent activity title refinement after provider content filter (activityId=${activityId})`
|
|
84
|
+
return null
|
|
85
|
+
})
|
|
86
|
+
: Effect.fail(error),
|
|
87
|
+
),
|
|
78
88
|
)
|
|
89
|
+
if (maybeRefinedTitle === null) {
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const refinedTitle = normalizeTitle(maybeRefinedTitle)
|
|
79
94
|
if (
|
|
80
95
|
!recentActivityService.isAgentTitleUseful({
|
|
81
96
|
currentTitle: candidate.title,
|