|
| 1 | +import { createHash } from "crypto"; |
1 | 2 | import { ProviderOptions } from "@ai-sdk/provider-utils"; |
2 | 3 | import { createLogger } from "@sourcebot/shared"; |
3 | 4 | import { LanguageModelProvider } from "@/features/chat/types"; |
@@ -105,19 +106,14 @@ interface CacheBreakSnapshot { |
105 | 106 | requestCount: number; |
106 | 107 | } |
107 | 108 |
|
108 | | -// Keyed by chatId. In-memory, best-effort: survives within a server process and |
109 | | -// is naturally bounded by the number of concurrent chats. Mirrors the in-memory |
110 | | -// caching pattern used elsewhere (e.g. `anthropicThinkingConfigCache`). |
| 109 | +// Keyed by chatId, in-memory, observability-only. Entries are never removed on chat |
| 110 | +// end, so the map is bounded by a FIFO cap (oldest insertion evicted first, below); |
| 111 | +// otherwise it grows with the cumulative count of distinct chats, not the concurrent count. |
| 112 | +const MAX_CACHE_BREAK_SNAPSHOTS = 10_000; |
111 | 113 | const cacheBreakSnapshots = new Map<string, CacheBreakSnapshot>(); |
112 | 114 |
|
113 | | -// Lightweight, stable, non-cryptographic hash (djb2). Observability only. |
114 | | -const hashString = (input: string): string => { |
115 | | - let hash = 5381; |
116 | | - for (let i = 0; i < input.length; i++) { |
117 | | - hash = ((hash << 5) + hash + input.charCodeAt(i)) | 0; |
118 | | - } |
119 | | - return (hash >>> 0).toString(36); |
120 | | -}; |
| 115 | +const hashString = (input: string): string => |
| 116 | + createHash('sha256').update(input).digest('hex').slice(0, 16); |
121 | 117 |
|
122 | 118 | /** |
123 | 119 | * Records the cache-relevant prefix signature for a chat and logs a warning when |
@@ -149,6 +145,14 @@ export const detectPromptCacheBreak = ({ |
149 | 145 | const requestCount = (prev?.requestCount ?? 0) + 1; |
150 | 146 | cacheBreakSnapshots.set(chatId, { signature, requestCount }); |
151 | 147 |
|
| 148 | + // FIFO eviction: once the map overflows, drop the oldest-inserted entry. |
| 149 | + if (cacheBreakSnapshots.size > MAX_CACHE_BREAK_SNAPSHOTS) { |
| 150 | + const oldestKey = cacheBreakSnapshots.keys().next().value; |
| 151 | + if (oldestKey !== undefined) { |
| 152 | + cacheBreakSnapshots.delete(oldestKey); |
| 153 | + } |
| 154 | + } |
| 155 | + |
152 | 156 | if (prev && prev.signature !== signature) { |
153 | 157 | logger.warn( |
154 | 158 | `Prompt cache break detected for chat ${chatId} (request #${requestCount}): the ` + |
|
0 commit comments