@lota-sdk/core 0.4.47 → 0.4.48
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 +92 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lota-sdk/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.48",
|
|
4
4
|
"files": [
|
|
5
5
|
"src",
|
|
6
6
|
"infrastructure/schema"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@ai-sdk/provider": "^3.0.9",
|
|
33
33
|
"@chat-adapter/slack": "^4.26.0",
|
|
34
34
|
"@chat-adapter/state-ioredis": "^4.26.0",
|
|
35
|
-
"@lota-sdk/shared": "0.4.
|
|
35
|
+
"@lota-sdk/shared": "0.4.48",
|
|
36
36
|
"@mendable/firecrawl-js": "^4.20.0",
|
|
37
37
|
"@surrealdb/node": "^3.0.3",
|
|
38
38
|
"ai": "^6.0.170",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
1
3
|
import { devToolsMiddleware } from '@ai-sdk/devtools'
|
|
2
4
|
import { createOpenAI } from '@ai-sdk/openai'
|
|
3
5
|
import type { JSONSchema7 } from '@ai-sdk/provider'
|
|
@@ -38,7 +40,16 @@ class AiGatewayStreamAttemptTag extends Context.Service<
|
|
|
38
40
|
const EXPECTED_GATEWAY_KEY_PREFIX = 'sk-bf-'
|
|
39
41
|
const AI_GATEWAY_VIRTUAL_KEY_HEADER = 'x-bf-vk'
|
|
40
42
|
const AI_GATEWAY_PASSTHROUGH_EXTRA_PARAMS_HEADER = 'x-bf-passthrough-extra-params'
|
|
43
|
+
const AI_GATEWAY_SESSION_ID_HEADER = 'x-bf-session-id'
|
|
44
|
+
const AI_GATEWAY_SESSION_TTL_HEADER = 'x-bf-session-ttl'
|
|
41
45
|
const AZURE_OPENAI_PROMPT_CACHE_RETENTION = '24h'
|
|
46
|
+
const AZURE_OPENAI_PROMPT_CACHE_SESSION_TTL = '24h'
|
|
47
|
+
const AZURE_OPENAI_PROMPT_CACHE_KEY_PREFIX = 'azpc'
|
|
48
|
+
const AZURE_OPENAI_PROMPT_CACHE_HASH_LENGTH = 48
|
|
49
|
+
const AZURE_OPENAI_PROMPT_CACHE_MAX_STRING_LENGTH = 120_000
|
|
50
|
+
const AZURE_OPENAI_PROMPT_CACHE_MAX_ARRAY_ITEMS = 80
|
|
51
|
+
const AZURE_OPENAI_PROMPT_CACHE_MAX_OBJECT_KEYS = 80
|
|
52
|
+
const AZURE_OPENAI_PROMPT_CACHE_MAX_DEPTH = 8
|
|
42
53
|
const AI_GATEWAY_TIMEOUT_MS = 360_000
|
|
43
54
|
const AI_GATEWAY_STREAM_IDLE_TIMEOUT_MS = 180_000
|
|
44
55
|
const AI_GATEWAY_MAX_RETRIES = 4
|
|
@@ -755,6 +766,77 @@ function isAzureOpenAiPromptCacheModel(modelId: string): boolean {
|
|
|
755
766
|
)
|
|
756
767
|
}
|
|
757
768
|
|
|
769
|
+
function hashAzureOpenAiPromptCacheValue(value: string): string {
|
|
770
|
+
return createHash('sha256').update(value).digest('hex').slice(0, AZURE_OPENAI_PROMPT_CACHE_HASH_LENGTH)
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function stablePromptCacheStringify(value: unknown): string {
|
|
774
|
+
return JSON.stringify(normalizePromptCacheValue(value, 0))
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function normalizePromptCacheValue(value: unknown, depth: number): unknown {
|
|
778
|
+
if (value === null || typeof value === 'boolean' || typeof value === 'number') return value
|
|
779
|
+
if (typeof value === 'string') {
|
|
780
|
+
return value.length > AZURE_OPENAI_PROMPT_CACHE_MAX_STRING_LENGTH
|
|
781
|
+
? value.slice(0, AZURE_OPENAI_PROMPT_CACHE_MAX_STRING_LENGTH)
|
|
782
|
+
: value
|
|
783
|
+
}
|
|
784
|
+
if (typeof value === 'bigint') return value.toString()
|
|
785
|
+
if (value instanceof Uint8Array) {
|
|
786
|
+
return {
|
|
787
|
+
type: 'uint8array',
|
|
788
|
+
length: value.byteLength,
|
|
789
|
+
sha256: createHash('sha256').update(value).digest('hex').slice(0, AZURE_OPENAI_PROMPT_CACHE_HASH_LENGTH),
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if (Array.isArray(value)) {
|
|
793
|
+
return value
|
|
794
|
+
.slice(0, AZURE_OPENAI_PROMPT_CACHE_MAX_ARRAY_ITEMS)
|
|
795
|
+
.map((item) => normalizePromptCacheValue(item, depth + 1))
|
|
796
|
+
}
|
|
797
|
+
if (!isRecord(value) || depth >= AZURE_OPENAI_PROMPT_CACHE_MAX_DEPTH) return null
|
|
798
|
+
|
|
799
|
+
return Object.fromEntries(
|
|
800
|
+
Object.entries(value)
|
|
801
|
+
.filter(([, item]) => item !== undefined && typeof item !== 'function' && typeof item !== 'symbol')
|
|
802
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
803
|
+
.slice(0, AZURE_OPENAI_PROMPT_CACHE_MAX_OBJECT_KEYS)
|
|
804
|
+
.map(([key, item]) => [key, normalizePromptCacheValue(item, depth + 1)]),
|
|
805
|
+
)
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function readExplicitOpenAiPromptCacheKey(openaiOptions: Record<string, unknown>): string | null {
|
|
809
|
+
const promptCacheKey = openaiOptions.promptCacheKey
|
|
810
|
+
return typeof promptCacheKey === 'string' && promptCacheKey.trim().length > 0 ? promptCacheKey.trim() : null
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function buildAzureOpenAiPromptCacheKey(params: AiGatewayCallOptions, modelId: string): string {
|
|
814
|
+
const payload = {
|
|
815
|
+
version: 1,
|
|
816
|
+
model: modelId.trim().toLowerCase(),
|
|
817
|
+
prompt: params.prompt,
|
|
818
|
+
responseFormat: params.responseFormat,
|
|
819
|
+
tools: params.tools,
|
|
820
|
+
}
|
|
821
|
+
return `${AZURE_OPENAI_PROMPT_CACHE_KEY_PREFIX}_${hashAzureOpenAiPromptCacheValue(stablePromptCacheStringify(payload))}`
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
function buildAzureOpenAiPromptCacheSessionId(promptCacheKey: string): string {
|
|
825
|
+
if (promptCacheKey.startsWith(`${AZURE_OPENAI_PROMPT_CACHE_KEY_PREFIX}_`)) return promptCacheKey
|
|
826
|
+
return `${AZURE_OPENAI_PROMPT_CACHE_KEY_PREFIX}_${hashAzureOpenAiPromptCacheValue(promptCacheKey)}`
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function withHeaderIfMissing(
|
|
830
|
+
headers: Record<string, string | undefined>,
|
|
831
|
+
name: string,
|
|
832
|
+
value: string,
|
|
833
|
+
): Record<string, string | undefined> {
|
|
834
|
+
for (const key of Object.keys(headers)) {
|
|
835
|
+
if (key.toLowerCase() === name.toLowerCase()) return headers
|
|
836
|
+
}
|
|
837
|
+
return { ...headers, [name]: value }
|
|
838
|
+
}
|
|
839
|
+
|
|
758
840
|
export function addAzureOpenAiPromptCacheRetention(
|
|
759
841
|
params: AiGatewayCallOptions,
|
|
760
842
|
modelId?: string,
|
|
@@ -765,12 +847,21 @@ export function addAzureOpenAiPromptCacheRetention(
|
|
|
765
847
|
|
|
766
848
|
const providerOptions = isRecord(params.providerOptions) ? { ...params.providerOptions } : {}
|
|
767
849
|
const openaiOptions = isRecord(providerOptions.openai) ? { ...providerOptions.openai } : {}
|
|
850
|
+
const promptCacheKey =
|
|
851
|
+
readExplicitOpenAiPromptCacheKey(openaiOptions) ?? buildAzureOpenAiPromptCacheKey(params, modelId)
|
|
852
|
+
const sessionId = buildAzureOpenAiPromptCacheSessionId(promptCacheKey)
|
|
853
|
+
const headersWithSession = withHeaderIfMissing(
|
|
854
|
+
withHeaderIfMissing({ ...params.headers }, AI_GATEWAY_SESSION_ID_HEADER, sessionId),
|
|
855
|
+
AI_GATEWAY_SESSION_TTL_HEADER,
|
|
856
|
+
AZURE_OPENAI_PROMPT_CACHE_SESSION_TTL,
|
|
857
|
+
)
|
|
768
858
|
|
|
769
859
|
return {
|
|
770
860
|
...params,
|
|
861
|
+
headers: headersWithSession,
|
|
771
862
|
providerOptions: {
|
|
772
863
|
...providerOptions,
|
|
773
|
-
openai: { ...openaiOptions, promptCacheRetention: AZURE_OPENAI_PROMPT_CACHE_RETENTION },
|
|
864
|
+
openai: { ...openaiOptions, promptCacheKey, promptCacheRetention: AZURE_OPENAI_PROMPT_CACHE_RETENTION },
|
|
774
865
|
} as AiGatewayCallOptions['providerOptions'],
|
|
775
866
|
}
|
|
776
867
|
}
|