@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lota-sdk/core",
3
- "version": "0.4.47",
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.47",
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
  }