@astro-minimax/ai 0.9.0 → 0.9.3

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.
Files changed (251) hide show
  1. package/README.md +108 -18
  2. package/dist/cache/global-cache.d.ts +6 -2
  3. package/dist/cache/global-cache.d.ts.map +1 -1
  4. package/dist/cache/global-cache.js +24 -9
  5. package/dist/cache/index.d.ts +7 -6
  6. package/dist/cache/index.d.ts.map +1 -1
  7. package/dist/cache/index.js +12 -4
  8. package/dist/cache/injection-cache.d.ts +36 -0
  9. package/dist/cache/injection-cache.d.ts.map +1 -0
  10. package/dist/cache/injection-cache.js +90 -0
  11. package/dist/cache/kv-adapter.d.ts.map +1 -1
  12. package/dist/cache/kv-adapter.js +2 -1
  13. package/dist/cache/memory-adapter.d.ts.map +1 -1
  14. package/dist/cache/memory-adapter.js +2 -1
  15. package/dist/cache/response-cache.d.ts +10 -5
  16. package/dist/cache/response-cache.d.ts.map +1 -1
  17. package/dist/cache/response-cache.js +18 -6
  18. package/dist/components/AIChatContainer.d.ts +2 -2
  19. package/dist/components/AIChatContainer.d.ts.map +1 -1
  20. package/dist/components/AIChatContainer.js +8 -920
  21. package/dist/components/ChatInput.d.ts +15 -0
  22. package/dist/components/ChatInput.d.ts.map +1 -0
  23. package/dist/components/ChatInput.js +72 -0
  24. package/dist/components/ChatPanel.d.ts +1 -1
  25. package/dist/components/ChatPanel.d.ts.map +1 -1
  26. package/dist/components/ChatPanel.js +210 -672
  27. package/dist/components/CodeBlock.d.ts +31 -0
  28. package/dist/components/CodeBlock.d.ts.map +1 -0
  29. package/dist/components/CodeBlock.js +143 -0
  30. package/dist/components/MarkmapBlock.d.ts +4 -0
  31. package/dist/components/MarkmapBlock.d.ts.map +1 -0
  32. package/dist/components/MarkmapBlock.js +180 -0
  33. package/dist/components/MermaidBlock.d.ts +4 -0
  34. package/dist/components/MermaidBlock.d.ts.map +1 -0
  35. package/dist/components/MermaidBlock.js +193 -0
  36. package/dist/components/MessageBubble.d.ts +21 -0
  37. package/dist/components/MessageBubble.d.ts.map +1 -0
  38. package/dist/components/MessageBubble.js +233 -0
  39. package/dist/components/ReasoningBlock.d.ts +6 -0
  40. package/dist/components/ReasoningBlock.d.ts.map +1 -0
  41. package/dist/components/ReasoningBlock.js +11 -0
  42. package/dist/components/RichText.d.ts +41 -0
  43. package/dist/components/RichText.d.ts.map +1 -0
  44. package/dist/components/RichText.js +202 -0
  45. package/dist/components/VizShared.d.ts +57 -0
  46. package/dist/components/VizShared.d.ts.map +1 -0
  47. package/dist/components/VizShared.js +233 -0
  48. package/dist/components/tool-auto-continue.d.ts +5 -0
  49. package/dist/components/tool-auto-continue.d.ts.map +1 -0
  50. package/dist/components/tool-auto-continue.js +33 -0
  51. package/dist/constants.d.ts +61 -0
  52. package/dist/constants.d.ts.map +1 -0
  53. package/dist/constants.js +72 -0
  54. package/dist/data/index.d.ts +4 -3
  55. package/dist/data/index.d.ts.map +1 -1
  56. package/dist/data/index.js +4 -10
  57. package/dist/data/knowledge-types.d.ts +8 -0
  58. package/dist/data/knowledge-types.d.ts.map +1 -0
  59. package/dist/data/knowledge-types.js +14 -0
  60. package/dist/data/metadata-loader.d.ts +4 -28
  61. package/dist/data/metadata-loader.d.ts.map +1 -1
  62. package/dist/data/metadata-loader.js +11 -34
  63. package/dist/data/types.d.ts +17 -2
  64. package/dist/data/types.d.ts.map +1 -1
  65. package/dist/extensions/index.d.ts +5 -0
  66. package/dist/extensions/index.d.ts.map +1 -0
  67. package/dist/extensions/index.js +24 -0
  68. package/dist/extensions/injector.d.ts +14 -0
  69. package/dist/extensions/injector.d.ts.map +1 -0
  70. package/dist/extensions/injector.js +146 -0
  71. package/dist/extensions/loader.d.ts +5 -0
  72. package/dist/extensions/loader.d.ts.map +1 -0
  73. package/dist/extensions/loader.js +45 -0
  74. package/dist/extensions/registry.d.ts +4 -0
  75. package/dist/extensions/registry.d.ts.map +1 -0
  76. package/dist/extensions/registry.js +144 -0
  77. package/dist/extensions/types.d.ts +126 -0
  78. package/dist/extensions/types.d.ts.map +1 -0
  79. package/dist/extensions/types.js +0 -0
  80. package/dist/fact-registry/prompt-injector.d.ts +1 -1
  81. package/dist/fact-registry/prompt-injector.d.ts.map +1 -1
  82. package/dist/fact-registry/prompt-injector.js +2 -1
  83. package/dist/index.d.ts +3 -2
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +3 -2
  86. package/dist/intelligence/citation-guard.d.ts +2 -13
  87. package/dist/intelligence/citation-guard.d.ts.map +1 -1
  88. package/dist/intelligence/citation-guard.js +52 -23
  89. package/dist/intelligence/evidence-analysis.d.ts +24 -16
  90. package/dist/intelligence/evidence-analysis.d.ts.map +1 -1
  91. package/dist/intelligence/evidence-analysis.js +118 -20
  92. package/dist/intelligence/evidence-budget.d.ts +13 -0
  93. package/dist/intelligence/evidence-budget.d.ts.map +1 -0
  94. package/dist/intelligence/evidence-budget.js +49 -0
  95. package/dist/intelligence/index.d.ts +10 -4
  96. package/dist/intelligence/index.d.ts.map +1 -1
  97. package/dist/intelligence/index.js +27 -3
  98. package/dist/intelligence/keyword-extract.d.ts +1 -1
  99. package/dist/intelligence/keyword-extract.d.ts.map +1 -1
  100. package/dist/intelligence/keyword-extract.js +5 -9
  101. package/dist/intelligence/request-interpretation.d.ts +40 -0
  102. package/dist/intelligence/request-interpretation.d.ts.map +1 -0
  103. package/dist/intelligence/request-interpretation.js +71 -0
  104. package/dist/intelligence/response-templates.d.ts +1 -0
  105. package/dist/intelligence/response-templates.d.ts.map +1 -1
  106. package/dist/intelligence/response-templates.js +13 -0
  107. package/dist/prompt/dynamic-layer.d.ts +1 -5
  108. package/dist/prompt/dynamic-layer.d.ts.map +1 -1
  109. package/dist/prompt/dynamic-layer.js +145 -9
  110. package/dist/prompt/prompt-builder.d.ts +1 -1
  111. package/dist/prompt/prompt-builder.d.ts.map +1 -1
  112. package/dist/prompt/prompt-builder.js +5 -1
  113. package/dist/prompt/semi-static-layer.d.ts +1 -1
  114. package/dist/prompt/semi-static-layer.d.ts.map +1 -1
  115. package/dist/prompt/semi-static-layer.js +22 -12
  116. package/dist/prompt/static-layer.d.ts.map +1 -1
  117. package/dist/prompt/static-layer.js +37 -4
  118. package/dist/prompt/types.d.ts +9 -4
  119. package/dist/prompt/types.d.ts.map +1 -1
  120. package/dist/provider-manager/base.d.ts +5 -1
  121. package/dist/provider-manager/base.d.ts.map +1 -1
  122. package/dist/provider-manager/base.js +22 -2
  123. package/dist/provider-manager/config.d.ts.map +1 -1
  124. package/dist/provider-manager/config.js +3 -2
  125. package/dist/provider-manager/index.d.ts +1 -1
  126. package/dist/provider-manager/index.d.ts.map +1 -1
  127. package/dist/provider-manager/index.js +1 -2
  128. package/dist/provider-manager/manager.d.ts +10 -1
  129. package/dist/provider-manager/manager.d.ts.map +1 -1
  130. package/dist/provider-manager/manager.js +26 -10
  131. package/dist/provider-manager/openai.d.ts +2 -2
  132. package/dist/provider-manager/openai.d.ts.map +1 -1
  133. package/dist/provider-manager/openai.js +19 -4
  134. package/dist/provider-manager/types.d.ts +18 -38
  135. package/dist/provider-manager/types.d.ts.map +1 -1
  136. package/dist/provider-manager/workers.d.ts +2 -2
  137. package/dist/provider-manager/workers.d.ts.map +1 -1
  138. package/dist/provider-manager/workers.js +15 -4
  139. package/dist/query/followup.d.ts +7 -0
  140. package/dist/query/followup.d.ts.map +1 -0
  141. package/dist/query/followup.js +46 -0
  142. package/dist/query/intent.d.ts +6 -0
  143. package/dist/query/intent.d.ts.map +1 -0
  144. package/dist/query/intent.js +137 -0
  145. package/dist/query/types.d.ts +8 -0
  146. package/dist/query/types.d.ts.map +1 -0
  147. package/dist/query/types.js +0 -0
  148. package/dist/search/hybrid-search.d.ts +111 -0
  149. package/dist/search/hybrid-search.d.ts.map +1 -0
  150. package/dist/search/hybrid-search.js +326 -0
  151. package/dist/search/index.d.ts +11 -9
  152. package/dist/search/index.d.ts.map +1 -1
  153. package/dist/search/index.js +46 -10
  154. package/dist/search/scoring.d.ts +18 -0
  155. package/dist/search/scoring.d.ts.map +1 -0
  156. package/dist/search/{search-utils.js → scoring.js} +14 -27
  157. package/dist/search/search-api.d.ts +16 -1
  158. package/dist/search/search-api.d.ts.map +1 -1
  159. package/dist/search/search-api.js +118 -15
  160. package/dist/search/search-index.d.ts +2 -2
  161. package/dist/search/search-index.d.ts.map +1 -1
  162. package/dist/search/search-index.js +4 -2
  163. package/dist/search/session-cache.d.ts +4 -10
  164. package/dist/search/session-cache.d.ts.map +1 -1
  165. package/dist/search/session-cache.js +12 -45
  166. package/dist/search/types.d.ts +28 -0
  167. package/dist/search/types.d.ts.map +1 -1
  168. package/dist/search/vector-reranker.d.ts +3 -3
  169. package/dist/search/vector-reranker.d.ts.map +1 -1
  170. package/dist/search/vector-reranker.js +14 -2
  171. package/dist/server/chat-handler.d.ts +86 -1
  172. package/dist/server/chat-handler.d.ts.map +1 -1
  173. package/dist/server/chat-handler.js +835 -401
  174. package/dist/server/chat-message-utils.d.ts +6 -0
  175. package/dist/server/chat-message-utils.d.ts.map +1 -0
  176. package/dist/server/chat-message-utils.js +40 -0
  177. package/dist/server/chat-utils.d.ts +30 -0
  178. package/dist/server/chat-utils.d.ts.map +1 -0
  179. package/dist/server/chat-utils.js +88 -0
  180. package/dist/server/dev-server.js +238 -101
  181. package/dist/server/env-config.d.ts +22 -0
  182. package/dist/server/env-config.d.ts.map +1 -0
  183. package/dist/server/env-config.js +25 -0
  184. package/dist/server/errors.d.ts +1 -0
  185. package/dist/server/errors.d.ts.map +1 -1
  186. package/dist/server/errors.js +14 -7
  187. package/dist/server/index.d.ts +2 -4
  188. package/dist/server/index.d.ts.map +1 -1
  189. package/dist/server/index.js +4 -25
  190. package/dist/server/metadata-init.d.ts +10 -5
  191. package/dist/server/metadata-init.d.ts.map +1 -1
  192. package/dist/server/metadata-init.js +78 -34
  193. package/dist/server/notify.d.ts +12 -11
  194. package/dist/server/notify.d.ts.map +1 -1
  195. package/dist/server/notify.js +46 -48
  196. package/dist/server/prompt-runtime.d.ts +60 -0
  197. package/dist/server/prompt-runtime.d.ts.map +1 -0
  198. package/dist/server/prompt-runtime.js +284 -0
  199. package/dist/server/stream-helpers.d.ts +30 -16
  200. package/dist/server/stream-helpers.d.ts.map +1 -1
  201. package/dist/server/stream-helpers.js +152 -15
  202. package/dist/server/types.d.ts +47 -12
  203. package/dist/server/types.d.ts.map +1 -1
  204. package/dist/structured-output/generator.d.ts +6 -0
  205. package/dist/structured-output/generator.d.ts.map +1 -0
  206. package/dist/structured-output/generator.js +164 -0
  207. package/dist/structured-output/index.d.ts +4 -0
  208. package/dist/structured-output/index.d.ts.map +1 -0
  209. package/dist/structured-output/index.js +6 -0
  210. package/dist/structured-output/schemas/evidence.d.ts +88 -0
  211. package/dist/structured-output/schemas/evidence.d.ts.map +1 -0
  212. package/dist/structured-output/schemas/evidence.js +65 -0
  213. package/dist/structured-output/types.d.ts +69 -0
  214. package/dist/structured-output/types.d.ts.map +1 -0
  215. package/dist/structured-output/types.js +0 -0
  216. package/dist/tools/action-tools.d.ts +63 -0
  217. package/dist/tools/action-tools.d.ts.map +1 -0
  218. package/dist/tools/action-tools.js +158 -0
  219. package/dist/tools/index.d.ts +2 -0
  220. package/dist/tools/index.d.ts.map +1 -0
  221. package/dist/tools/index.js +30 -0
  222. package/dist/utils/i18n.d.ts +1 -1
  223. package/dist/utils/i18n.d.ts.map +1 -1
  224. package/dist/utils/i18n.js +1 -1
  225. package/dist/utils/logger.d.ts +11 -0
  226. package/dist/utils/logger.d.ts.map +1 -0
  227. package/dist/utils/logger.js +36 -0
  228. package/dist/utils/text.d.ts +11 -0
  229. package/dist/utils/text.d.ts.map +1 -0
  230. package/dist/utils/text.js +87 -0
  231. package/dist/utils/url.d.ts +19 -0
  232. package/dist/utils/url.d.ts.map +1 -0
  233. package/dist/utils/url.js +13 -0
  234. package/package.json +46 -12
  235. package/dist/intelligence/intent-detect.d.ts +0 -40
  236. package/dist/intelligence/intent-detect.d.ts.map +0 -1
  237. package/dist/intelligence/intent-detect.js +0 -93
  238. package/dist/providers/index.d.ts +0 -2
  239. package/dist/providers/index.d.ts.map +0 -1
  240. package/dist/providers/index.js +0 -5
  241. package/dist/search/search-utils.d.ts +0 -47
  242. package/dist/search/search-utils.d.ts.map +0 -1
  243. package/dist/stream/index.d.ts +0 -3
  244. package/dist/stream/index.d.ts.map +0 -1
  245. package/dist/stream/index.js +0 -8
  246. package/dist/stream/mock-stream.d.ts +0 -12
  247. package/dist/stream/mock-stream.d.ts.map +0 -1
  248. package/dist/stream/mock-stream.js +0 -26
  249. package/dist/stream/response.d.ts +0 -10
  250. package/dist/stream/response.d.ts.map +0 -1
  251. package/dist/stream/response.js +0 -21
package/README.md CHANGED
@@ -31,11 +31,81 @@ Vendor-agnostic AI integration package with full RAG pipeline for astro-minimax
31
31
  | `server/` | Reusable API handlers (`handleChatRequest`, `initializeMetadata`) |
32
32
  | `provider-manager/` | Multi-provider management with priority, failover, health tracking |
33
33
  | `search/` | In-memory article/project search with session caching |
34
- | `intelligence/` | Keyword extraction, evidence analysis, citation guard |
34
+ | `intelligence/` | Keyword extraction, evidence analysis, citation guard, answer mode, dynamic evidence budget |
35
35
  | `prompt/` | Three-layer system prompt builder (static → semi-static → dynamic) |
36
- | `data/` | Build-time metadata loading (summaries, author context, voice profile) |
37
- | `stream/` | Stream helpers and response utilities |
36
+ | `data/` | Bundle-backed runtime metadata loading and shared data types |
38
37
  | `components/` | Preact UI components (ChatPanel, AIChatWidget, AIChatContainer) |
38
+ | `extensions/` | Search/prompt extensions and semantic fallback rules |
39
+ | `structured-output/`| Schema-validated structured generation helpers |
40
+ | `cache/` | Response/session/injection cache utilities |
41
+ | `fact-registry/` | Verified facts used for grounded prompt assembly |
42
+ | `tools/` | Runtime tool registry and built-in action/search tools |
43
+
44
+ ## Features
45
+
46
+ ### Dynamic Evidence Budget
47
+
48
+ The system dynamically adjusts retrieval and analysis resources based on query complexity:
49
+
50
+ | Complexity | Max Articles | Summary Length | Key Points | Deep Content |
51
+ |------------|--------------|----------------|------------|--------------|
52
+ | `simple` | 4 | 48 chars | 2 | No |
53
+ | `moderate` | 6 | 56 chars | 3 | Yes |
54
+ | `complex` | 8 | 64 chars | 4 | Yes |
55
+
56
+ Budget is further adjusted by **answer mode** (count, list, opinion, recommendation):
57
+
58
+ ```typescript
59
+ import { getEvidenceBudget, applyBudgetToArticles } from '@astro-minimax/ai/intelligence';
60
+
61
+ const budget = getEvidenceBudget('moderate', 'list');
62
+ // → { maxArticles: 8, summaryMaxLength: 80, ... }
63
+
64
+ const trimmedArticles = applyBudgetToArticles(articles, budget);
65
+ ```
66
+
67
+ ### Answer Mode Detection
68
+
69
+ Automatically detects the expected response format from user queries:
70
+
71
+ | Mode | Trigger Patterns | Response Style |
72
+ |-----------------|-------------------------------|-----------------------------------|
73
+ | `fact` | "是什么", "what is" | Conclusion first, then evidence |
74
+ | `count` | "多少", "how many" | Number in first sentence |
75
+ | `list` | "哪些", "what are" | 2-6 items directly |
76
+ | `opinion` | "怎么看", "what do you think" | "I think..." + 2-3 points |
77
+ | `recommendation`| "推荐", "suggest" | 2-4 recommendations + reasons |
78
+
79
+ Answer mode hints are injected into the dynamic prompt layer, guiding the LLM toward the appropriate format.
80
+
81
+ ### Reading Time Display
82
+
83
+ Article reading time is now displayed in the dynamic prompt layer:
84
+
85
+ ```
86
+ **[Article Title](/posts/article)**
87
+ 阅读时间:约 5 分钟
88
+ 摘要:Article summary...
89
+ ```
90
+
91
+ ### Enhanced Citation Guard
92
+
93
+ Improved URL validation prevents hallucinated links:
94
+
95
+ - **Scheme whitelist**: Only `http://` and `https://` allowed
96
+ - **Domain validation**: Blocks localhost, private IPs, internal networks
97
+ - **XSS prevention**: Sanitizes dangerous URL patterns
98
+
99
+ ```typescript
100
+ import { createCitationGuardTransform } from '@astro-minimax/ai/intelligence';
101
+
102
+ const guard = createCitationGuardTransform({
103
+ articles,
104
+ projects,
105
+ siteUrl: 'https://example.com',
106
+ onApplied: ({ actions }) => console.log('Rewrote:', actions),
107
+ });
108
+ ```
39
109
 
40
110
  ## Installation
41
111
 
@@ -77,13 +147,13 @@ export const SITE = {
77
147
 
78
148
  ### Response Cache Configuration
79
149
 
80
- | Variable | Default | Description |
81
- | ---------------------------------- | ------- | ----------------------------------------- |
82
- | `AI_RESPONSE_CACHE_ENABLED` | `false` | Enable AI response caching |
83
- | `AI_RESPONSE_CACHE_TTL` | `3600` | Cache TTL in seconds (1 hour) |
84
- | `AI_RESPONSE_CACHE_PLAYBACK_DELAY` | `20` | Delay between chunks during playback (ms) |
85
- | `AI_RESPONSE_CACHE_CHUNK_SIZE` | `15` | Characters per chunk during playback |
86
- | `AI_RESPONSE_CACHE_THINKING_DELAY` | `5` | Delay for thinking content playback (ms) |
150
+ | Variable | Default | Description |
151
+ | --------------------------- | ------- | ----------------------------------------- |
152
+ | `AI_CACHE_ENABLED` | `false` | Enable AI response caching |
153
+ | `AI_CACHE_TTL` | `3600` | Cache TTL in seconds (1 hour) |
154
+ | `AI_CACHE_PLAYBACK_DELAY` | `20` | Delay between chunks during playback (ms) |
155
+ | `AI_CACHE_CHUNK_SIZE` | `15` | Characters per chunk during playback |
156
+ | `AI_CACHE_THINKING_DELAY` | `5` | Delay for thinking content playback (ms) |
87
157
 
88
158
  When enabled, the system caches complete AI responses (including thinking/reasoning content) for public questions like "What tech stack does this blog use?". Subsequent identical queries are served from cache with simulated streaming playback, reducing API costs and response time.
89
159
 
@@ -96,12 +166,11 @@ The server module provides reusable request handlers, decoupled from any specifi
96
166
  ```typescript
97
167
  // functions/api/chat.ts
98
168
  import { handleChatRequest, initializeMetadata } from '@astro-minimax/ai/server';
99
- import summaries from '../../datas/ai-summaries.json';
100
- import authorContext from '../../datas/author-context.json';
101
- import voiceProfile from '../../datas/voice-profile.json';
169
+ import knowledgeBundle from '../../datas/knowledge/runtime/knowledge-bundle.json';
102
170
 
103
171
  export const onRequest: PagesFunction = async (context) => {
104
- initializeMetadata({ summaries, authorContext, voiceProfile }, context.env);
172
+ initializeMetadata({ knowledgeBundle }, context.env);
173
+
105
174
  return handleChatRequest({ env: context.env, request: context.request });
106
175
  };
107
176
  ```
@@ -213,11 +282,32 @@ Core chat UI built on `useChat` from `@ai-sdk/react`:
213
282
  | ---------------- | --------------------------------------------------------------- |
214
283
  | `.` | All modules |
215
284
  | `./server` | `handleChatRequest`, `initializeMetadata`, error helpers, types |
216
- | `./providers` | Mock response/stream utilities |
217
285
  | `./middleware` | Rate limiting |
218
286
  | `./search` | Article/project search, session cache |
219
- | `./intelligence` | Keyword extraction, evidence analysis, citation guard |
287
+ | `./intelligence` | Keyword extraction, evidence analysis, citation guard, answer mode, evidence budget |
220
288
  | `./prompt` | System prompt builder |
289
+ | `./cache` | Cache adapters and response/session cache utilities |
221
290
  | `./data` | Metadata loading |
222
- | `./stream` | Stream utilities |
223
- | `./components/*` | Astro/Preact components |
291
+ | `./fact-registry`| Verified facts registry |
292
+ | `./extensions` | Extension registry, loader, and injector |
293
+ | `./structured-output` | Structured output helpers |
294
+ | `./tools` | Tool registry and built-in AI tools |
295
+ | `./components/ChatPanel` | Preact chat panel component |
296
+ | `./components/AIChatContainer` | Preact chat container component |
297
+ | `./components/AIChatWidget.astro` | Astro chat widget entry point |
298
+
299
+ ## Testing
300
+
301
+ The package includes comprehensive unit tests with Vitest:
302
+
303
+ ```bash
304
+ cd packages/ai
305
+ pnpm test
306
+ ```
307
+
308
+ Test coverage includes:
309
+ - Citation guard (10 tests)
310
+ - Intent detection (7 tests)
311
+ - Keyword extraction (7 tests)
312
+ - Evidence analysis (7 tests)
313
+ - Evidence budget (5 tests)
@@ -1,5 +1,5 @@
1
- import type { CacheAdapter, CachedSearchContext } from './types.js';
2
- export type PublicQuestionType = 'tech' | 'recommend' | 'build' | 'summary' | 'author' | 'about';
1
+ import type { CacheAdapter, CachedSearchContext } from "./types.js";
2
+ export type PublicQuestionType = "tech" | "recommend" | "build" | "summary" | "author" | "about";
3
3
  export interface PublicQuestionPattern {
4
4
  type: PublicQuestionType;
5
5
  keywords: string[];
@@ -18,14 +18,18 @@ export declare function detectPublicQuestion(query: string): DetectedPublicQuest
18
18
  export declare function buildGlobalCacheKey(type: PublicQuestionType, context?: {
19
19
  articleSlug?: string;
20
20
  lang?: string;
21
+ queryKey?: string;
21
22
  }): string;
23
+ export declare function normalizePublicCacheQuery(query: string): string;
22
24
  export declare function getGlobalSearchCache(cache: CacheAdapter, type: PublicQuestionType, context?: {
23
25
  articleSlug?: string;
24
26
  lang?: string;
27
+ queryKey?: string;
25
28
  }): Promise<CachedSearchContext | null>;
26
29
  export declare function setGlobalSearchCache(cache: CacheAdapter, type: PublicQuestionType, data: CachedSearchContext, ttl: number, context?: {
27
30
  articleSlug?: string;
28
31
  lang?: string;
32
+ queryKey?: string;
29
33
  }): Promise<void>;
30
34
  export declare function getGlobalCacheTTL(type: PublicQuestionType): number;
31
35
  //# sourceMappingURL=global-cache.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"global-cache.d.ts","sourceRoot":"","sources":["../../src/cache/global-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAC1B,MAAM,GACN,WAAW,GACX,OAAO,GACP,SAAS,GACT,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,eAAO,MAAM,wBAAwB,EAAE,qBAAqB,EAiF3D,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,kBAAkB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAkCjF;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD,MAAM,CAYR;AAUD,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAIrC;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,kBAAkB,EACxB,IAAI,EAAE,mBAAmB,EACzB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAGlE"}
1
+ {"version":3,"file":"global-cache.d.ts","sourceRoot":"","sources":["../../src/cache/global-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAC1B,MAAM,GACN,WAAW,GACX,OAAO,GACP,SAAS,GACT,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,eAAO,MAAM,wBAAwB,EAAE,qBAAqB,EAoF3D,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,kBAAkB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,GACZ,sBAAsB,GAAG,IAAI,CAkC/B;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE,MAAM,CAgBR;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM/D;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAOrC;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,kBAAkB,EACxB,IAAI,EAAE,mBAAmB,EACzB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAGlE"}
@@ -1,7 +1,15 @@
1
1
  const PUBLIC_QUESTION_PATTERNS = [
2
2
  {
3
3
  type: "tech",
4
- keywords: ["\u6280\u672F\u6808", "\u6280\u672F", "\u6846\u67B6", "\u7528\u4E86\u4EC0\u4E48", "built with", "tech stack", "framework"],
4
+ keywords: [
5
+ "\u6280\u672F\u6808",
6
+ "\u6280\u672F",
7
+ "\u6846\u67B6",
8
+ "\u7528\u4E86\u4EC0\u4E48",
9
+ "built with",
10
+ "tech stack",
11
+ "framework"
12
+ ],
5
13
  patterns: [
6
14
  /这个博客用了什么/,
7
15
  /博客.*技术栈/,
@@ -70,18 +78,13 @@ const PUBLIC_QUESTION_PATTERNS = [
70
78
  {
71
79
  type: "about",
72
80
  keywords: ["\u5173\u4E8E", "\u4ECB\u7ECD", "about", "intro"],
73
- patterns: [
74
- /关于.*博客/,
75
- /博客介绍/,
76
- /about.*blog/,
77
- /介绍一下/
78
- ],
81
+ patterns: [/关于.*博客/, /博客介绍/, /about.*blog/, /介绍一下/],
79
82
  ttl: 86400,
80
83
  needsContext: false
81
84
  }
82
85
  ];
83
86
  function detectPublicQuestion(query) {
84
- const normalized = normalizeQuery(query);
87
+ const normalized = normalizePublicCacheQuery(query);
85
88
  let bestMatch = null;
86
89
  let bestScore = 0;
87
90
  for (const pattern of PUBLIC_QUESTION_PATTERNS) {
@@ -117,29 +120,41 @@ function buildGlobalCacheKey(type, context) {
117
120
  if (context?.lang) {
118
121
  parts.push(context.lang);
119
122
  }
123
+ if (context?.queryKey) {
124
+ parts.push(context.queryKey);
125
+ }
120
126
  return parts.join(":");
121
127
  }
122
- function normalizeQuery(query) {
128
+ function normalizePublicCacheQuery(query) {
123
129
  return query.toLowerCase().replace(/[??!!。,,.]/g, "").replace(/\s+/g, " ").trim();
124
130
  }
125
131
  async function getGlobalSearchCache(cache, type, context) {
126
132
  const key = buildGlobalCacheKey(type, context);
127
133
  const entry = await cache.get(key);
134
+ log.debug(
135
+ `getGlobalSearchCache: key=${key}, hit=${Boolean(entry?.value)}, adapter=${cache.name}`
136
+ );
128
137
  return entry?.value ?? null;
129
138
  }
130
139
  async function setGlobalSearchCache(cache, type, data, ttl, context) {
131
140
  const key = buildGlobalCacheKey(type, context);
132
141
  await cache.set(key, data, { ttl });
142
+ log.debug(
143
+ `setGlobalSearchCache: key=${key}, articles=${data.articles.length}, projects=${data.projects.length}, ttl=${ttl}, adapter=${cache.name}`
144
+ );
133
145
  }
134
146
  function getGlobalCacheTTL(type) {
135
147
  const pattern = PUBLIC_QUESTION_PATTERNS.find((p) => p.type === type);
136
148
  return pattern?.ttl ?? 3600;
137
149
  }
150
+ import { createLogger } from "../utils/logger.js";
151
+ const log = createLogger("global-cache");
138
152
  export {
139
153
  PUBLIC_QUESTION_PATTERNS,
140
154
  buildGlobalCacheKey,
141
155
  detectPublicQuestion,
142
156
  getGlobalCacheTTL,
143
157
  getGlobalSearchCache,
158
+ normalizePublicCacheQuery,
144
159
  setGlobalSearchCache
145
160
  };
@@ -1,8 +1,9 @@
1
- export { MemoryCacheAdapter, type MemoryCacheOptions, } from './memory-adapter.js';
2
- export { KVCacheAdapter, type KVCacheOptions, } from './kv-adapter.js';
3
- export { createCacheKeyBuilder, type CacheAdapter, type CacheEntry, type CacheEntryMetadata, type CacheSetOptions, type CacheGetOptions, type CacheManagerConfig, type CacheEnv, type CachedSearchContext, type CacheResult, type CacheKeyBuilder, } from './types.js';
4
- export { detectPublicQuestion, buildGlobalCacheKey, getGlobalSearchCache, setGlobalSearchCache, getGlobalCacheTTL, PUBLIC_QUESTION_PATTERNS, type PublicQuestionType, type PublicQuestionPattern, type DetectedPublicQuestion, } from './global-cache.js';
5
- export { getResponseCache, setResponseCache, deleteResponseCache, getResponseCacheConfig, buildResponseCacheKey, createResponsePlaybackGenerator, DEFAULT_RESPONSE_CACHE_CONFIG, type CachedAIResponse, type ResponseCacheConfig, type ResponseCacheEnv, type PlaybackChunk, } from './response-cache.js';
6
- import type { CacheAdapter, CacheEnv, CacheManagerConfig } from './types.js';
1
+ export { MemoryCacheAdapter, type MemoryCacheOptions, } from "./memory-adapter.js";
2
+ export { KVCacheAdapter, type KVCacheOptions } from "./kv-adapter.js";
3
+ export { createCacheKeyBuilder, type CacheAdapter, type CacheEntry, type CacheEntryMetadata, type CacheSetOptions, type CacheGetOptions, type CacheManagerConfig, type CacheEnv, type CachedSearchContext, type CacheResult, type CacheKeyBuilder, } from "./types.js";
4
+ export { detectPublicQuestion, buildGlobalCacheKey, normalizePublicCacheQuery, getGlobalSearchCache, setGlobalSearchCache, getGlobalCacheTTL, PUBLIC_QUESTION_PATTERNS, type PublicQuestionType, type PublicQuestionPattern, type DetectedPublicQuestion, } from "./global-cache.js";
5
+ export { getResponseCache, setResponseCache, deleteResponseCache, getResponseCacheConfig, buildResponseCacheKey, createResponsePlaybackGenerator, DEFAULT_RESPONSE_CACHE_CONFIG, type CachedAIResponse, type ResponseCacheConfig, type ResponseCacheEnv, type PlaybackChunk, } from "./response-cache.js";
6
+ export { injectionCache, type InjectionCacheEntry } from "./injection-cache.js";
7
+ import type { CacheAdapter, CacheEnv, CacheManagerConfig } from "./types.js";
7
8
  export declare function createCacheAdapter(env: CacheEnv, config?: CacheManagerConfig): CacheAdapter;
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,cAAc,EACd,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,+BAA+B,EAC/B,6BAA6B,EAC7B,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAc7E,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,QAAQ,EACb,MAAM,CAAC,EAAE,kBAAkB,GAC1B,YAAY,CAcd"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,EACL,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,QAAQ,EACb,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,yBAAyB,EACzB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,+BAA+B,EAC/B,6BAA6B,EAC7B,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAIhF,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAsB7E,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,QAAQ,EACb,MAAM,CAAC,EAAE,kBAAkB,GAC1B,YAAY,CAiBd"}
@@ -1,15 +1,14 @@
1
1
  import {
2
2
  MemoryCacheAdapter
3
3
  } from "./memory-adapter.js";
4
- import {
5
- KVCacheAdapter
6
- } from "./kv-adapter.js";
4
+ import { KVCacheAdapter } from "./kv-adapter.js";
7
5
  import {
8
6
  createCacheKeyBuilder
9
7
  } from "./types.js";
10
8
  import {
11
9
  detectPublicQuestion,
12
10
  buildGlobalCacheKey,
11
+ normalizePublicCacheQuery,
13
12
  getGlobalSearchCache,
14
13
  setGlobalSearchCache,
15
14
  getGlobalCacheTTL,
@@ -24,9 +23,13 @@ import {
24
23
  createResponsePlaybackGenerator,
25
24
  DEFAULT_RESPONSE_CACHE_CONFIG
26
25
  } from "./response-cache.js";
26
+ import { injectionCache } from "./injection-cache.js";
27
27
  import { MemoryCacheAdapter as MemoryCacheAdapter2 } from "./memory-adapter.js";
28
28
  import { KVCacheAdapter as KVCacheAdapter2 } from "./kv-adapter.js";
29
- const DEFAULT_TTL = 600;
29
+ import { CACHE } from "../constants.js";
30
+ import { createLogger } from "../utils/logger.js";
31
+ const log = createLogger("cache");
32
+ const DEFAULT_TTL = CACHE.DEFAULT_TTL;
30
33
  const DEFAULT_MAX_ENTRIES = 400;
31
34
  let globalMemoryCache = null;
32
35
  function getGlobalMemoryCache(ttl, maxEntries) {
@@ -39,12 +42,15 @@ function createCacheAdapter(env, config) {
39
42
  const ttl = config?.defaultTtl ?? parseTtl(env.CACHE_TTL) ?? DEFAULT_TTL;
40
43
  const maxEntries = config?.maxEntries ?? DEFAULT_MAX_ENTRIES;
41
44
  if (isCacheDisabled(env)) {
45
+ log.debug(`createCacheAdapter: cache disabled, using memory ttl=${ttl}`);
42
46
  return getGlobalMemoryCache(ttl, maxEntries);
43
47
  }
44
48
  const kvBinding = getKVBinding(env);
45
49
  if (kvBinding) {
50
+ log.debug(`createCacheAdapter: using kv adapter ttl=${ttl}`);
46
51
  return new KVCacheAdapter2(kvBinding, { defaultTtl: ttl });
47
52
  }
53
+ log.debug(`createCacheAdapter: using memory adapter ttl=${ttl}`);
48
54
  return getGlobalMemoryCache(ttl, maxEntries);
49
55
  }
50
56
  function isCacheDisabled(env) {
@@ -91,6 +97,8 @@ export {
91
97
  getGlobalSearchCache,
92
98
  getResponseCache,
93
99
  getResponseCacheConfig,
100
+ injectionCache,
101
+ normalizePublicCacheQuery,
94
102
  setGlobalSearchCache,
95
103
  setResponseCache
96
104
  };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * 注入缓存管理
3
+ *
4
+ * 用于多轮对话中去重已注入的段落,避免重复注入浪费 token。
5
+ */
6
+ export interface InjectionCacheEntry {
7
+ sessionId: string;
8
+ injectedChunks: Set<string>;
9
+ createdAt: number;
10
+ updatedAt: number;
11
+ }
12
+ declare class InjectionCacheManager {
13
+ private caches;
14
+ private ttl;
15
+ constructor(ttl?: number);
16
+ get(sessionId: string): InjectionCacheEntry | undefined;
17
+ getOrCreate(sessionId: string): InjectionCacheEntry;
18
+ filterNewChunks(sessionId: string, chunks: Array<{
19
+ id: string;
20
+ content: string;
21
+ }>): Array<{
22
+ id: string;
23
+ content: string;
24
+ }>;
25
+ markAsInjected(sessionId: string, chunkIds: string[]): void;
26
+ cleanup(): void;
27
+ clear(sessionId?: string): void;
28
+ size(): number;
29
+ getStats(): {
30
+ totalSessions: number;
31
+ totalInjectedChunks: number;
32
+ };
33
+ }
34
+ export declare const injectionCache: InjectionCacheManager;
35
+ export {};
36
+ //# sourceMappingURL=injection-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"injection-cache.d.ts","sourceRoot":"","sources":["../../src/cache/injection-cache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,cAAM,qBAAqB;IACzB,OAAO,CAAC,MAAM,CAA+C;IAC7D,OAAO,CAAC,GAAG,CAAS;gBAER,GAAG,GAAE,MAAoB;IAIrC,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAKvD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB;IAiBnD,eAAe,CACb,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,GAC7C,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAazC,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI;IAW3D,OAAO,IAAI,IAAI;IAqBf,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAQ/B,IAAI,IAAI,MAAM;IAId,QAAQ,IAAI;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAA;KAAE;CAUnE;AAED,eAAO,MAAM,cAAc,uBAA8B,CAAC"}
@@ -0,0 +1,90 @@
1
+ const DEFAULT_TTL = 10 * 60 * 1e3;
2
+ const MAX_CACHE_SIZE = 100;
3
+ class InjectionCacheManager {
4
+ caches = /* @__PURE__ */ new Map();
5
+ ttl;
6
+ constructor(ttl = DEFAULT_TTL) {
7
+ this.ttl = ttl;
8
+ }
9
+ get(sessionId) {
10
+ this.cleanup();
11
+ return this.caches.get(sessionId);
12
+ }
13
+ getOrCreate(sessionId) {
14
+ this.cleanup();
15
+ let entry = this.caches.get(sessionId);
16
+ if (!entry) {
17
+ entry = {
18
+ sessionId,
19
+ injectedChunks: /* @__PURE__ */ new Set(),
20
+ createdAt: Date.now(),
21
+ updatedAt: Date.now()
22
+ };
23
+ this.caches.set(sessionId, entry);
24
+ }
25
+ return entry;
26
+ }
27
+ filterNewChunks(sessionId, chunks) {
28
+ const entry = this.getOrCreate(sessionId);
29
+ const newChunks = chunks.filter(
30
+ (chunk) => !entry.injectedChunks.has(chunk.id)
31
+ );
32
+ log.debug(
33
+ `filterNewChunks: session=${sessionId}, incoming=${chunks.length}, new=${newChunks.length}, existing=${entry.injectedChunks.size}`
34
+ );
35
+ return newChunks;
36
+ }
37
+ markAsInjected(sessionId, chunkIds) {
38
+ const entry = this.getOrCreate(sessionId);
39
+ for (const id of chunkIds) {
40
+ entry.injectedChunks.add(id);
41
+ }
42
+ entry.updatedAt = Date.now();
43
+ log.debug(
44
+ `markAsInjected: session=${sessionId}, added=${chunkIds.length}, total=${entry.injectedChunks.size}`
45
+ );
46
+ }
47
+ cleanup() {
48
+ const now = Date.now();
49
+ for (const [key, entry] of this.caches) {
50
+ if (now - entry.updatedAt > this.ttl) {
51
+ this.caches.delete(key);
52
+ }
53
+ }
54
+ if (this.caches.size > MAX_CACHE_SIZE) {
55
+ const entries = [...this.caches.entries()].sort(
56
+ (a, b) => a[1].updatedAt - b[1].updatedAt
57
+ );
58
+ const toDelete = entries.slice(0, this.caches.size - MAX_CACHE_SIZE);
59
+ for (const [key] of toDelete) {
60
+ this.caches.delete(key);
61
+ }
62
+ }
63
+ }
64
+ clear(sessionId) {
65
+ if (sessionId) {
66
+ this.caches.delete(sessionId);
67
+ } else {
68
+ this.caches.clear();
69
+ }
70
+ }
71
+ size() {
72
+ return this.caches.size;
73
+ }
74
+ getStats() {
75
+ let totalInjectedChunks = 0;
76
+ for (const entry of this.caches.values()) {
77
+ totalInjectedChunks += entry.injectedChunks.size;
78
+ }
79
+ return {
80
+ totalSessions: this.caches.size,
81
+ totalInjectedChunks
82
+ };
83
+ }
84
+ }
85
+ const injectionCache = new InjectionCacheManager();
86
+ import { createLogger } from "../utils/logger.js";
87
+ const log = createLogger("injection-cache");
88
+ export {
89
+ injectionCache
90
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"kv-adapter.d.ts","sourceRoot":"","sources":["../../src/cache/kv-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EAEV,eAAe,EACf,eAAe,EAChB,MAAM,YAAY,CAAC;AAOpB,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAKD,qBAAa,cAAe,YAAW,YAAY;IACjD,QAAQ,CAAC,IAAI,mBAAmB;IAChC,OAAO,CAAC,EAAE,CAAc;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAS;gBAEX,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,cAAc;IAMrD,OAAO,CAAC,QAAQ;IAIV,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IA0B7E,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BvE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAerC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUlC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IASrC,OAAO,IAAI,IAAI;CAGhB"}
1
+ {"version":3,"file":"kv-adapter.d.ts","sourceRoot":"","sources":["../../src/cache/kv-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EAEV,eAAe,EACf,eAAe,EAChB,MAAM,YAAY,CAAC;AAOpB,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD,qBAAa,cAAe,YAAW,YAAY;IACjD,QAAQ,CAAC,IAAI,mBAAmB;IAChC,OAAO,CAAC,EAAE,CAAc;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAS;gBAEX,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,cAAc;IAMrD,OAAO,CAAC,QAAQ;IAIV,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IA0B7E,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BvE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAerC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUlC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IASrC,OAAO,IAAI,IAAI;CAGhB"}
@@ -1,4 +1,5 @@
1
- const DEFAULT_TTL_SECONDS = 600;
1
+ import { CACHE } from "../constants.js";
2
+ const DEFAULT_TTL_SECONDS = CACHE.DEFAULT_TTL;
2
3
  const MIN_TTL_SECONDS = 60;
3
4
  class KVCacheAdapter {
4
5
  name = "cloudflare-kv";
@@ -1 +1 @@
1
- {"version":3,"file":"memory-adapter.d.ts","sourceRoot":"","sources":["../../src/cache/memory-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EAEV,eAAe,EACf,eAAe,EAChB,MAAM,YAAY,CAAC;AAQpB,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAKD,qBAAa,kBAAmB,YAAW,YAAY;IACrD,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAC,CAAiC;gBAE1C,OAAO,CAAC,EAAE,kBAAkB;IAYlC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAmB9E,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBvE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYlC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,QAAQ;IAYhB,OAAO,IAAI,IAAI;CAOhB"}
1
+ {"version":3,"file":"memory-adapter.d.ts","sourceRoot":"","sources":["../../src/cache/memory-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EAEV,eAAe,EACf,eAAe,EAChB,MAAM,YAAY,CAAC;AAQpB,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAOD,qBAAa,kBAAmB,YAAW,YAAY;IACrD,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAC,CAAiC;gBAE1C,OAAO,CAAC,EAAE,kBAAkB;IAYlC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAmB9E,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBvE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYlC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,QAAQ;IAYhB,OAAO,IAAI,IAAI;CAOhB"}
@@ -1,5 +1,6 @@
1
1
  const DEFAULT_MAX_ENTRIES = 400;
2
- const DEFAULT_TTL_SECONDS = 600;
2
+ import { CACHE } from "../constants.js";
3
+ const DEFAULT_TTL_SECONDS = CACHE.DEFAULT_TTL;
3
4
  class MemoryCacheAdapter {
4
5
  name = "memory";
5
6
  cache = /* @__PURE__ */ new Map();
@@ -1,12 +1,13 @@
1
- import type { CacheAdapter } from './types.js';
2
- import type { ArticleContext, ProjectContext } from '../search/types.js';
3
- import type { PublicQuestionType } from './global-cache.js';
1
+ import type { CacheAdapter } from "./types.js";
2
+ import type { ArticleContext, ProjectContext, SourceSelection } from "../search/types.js";
3
+ import type { PublicQuestionType } from "./global-cache.js";
4
4
  export interface CachedAIResponse {
5
5
  query: string;
6
6
  thinking?: string;
7
7
  response: string;
8
8
  articles: ArticleContext[];
9
9
  projects: ProjectContext[];
10
+ sources: SourceSelection[];
10
11
  lang: string;
11
12
  model?: string;
12
13
  updatedAt: number;
@@ -24,22 +25,26 @@ export declare function getResponseCacheConfig(env: Record<string, unknown>): Re
24
25
  export declare function buildResponseCacheKey(type: PublicQuestionType, context?: {
25
26
  articleSlug?: string;
26
27
  lang?: string;
28
+ queryKey?: string;
27
29
  }): string;
28
30
  export declare function getResponseCache(cache: CacheAdapter, type: PublicQuestionType, context?: {
29
31
  articleSlug?: string;
30
32
  lang?: string;
33
+ queryKey?: string;
31
34
  }): Promise<CachedAIResponse | null>;
32
35
  export declare function setResponseCache(cache: CacheAdapter, type: PublicQuestionType, data: CachedAIResponse, ttl: number, context?: {
33
36
  articleSlug?: string;
34
37
  lang?: string;
38
+ queryKey?: string;
35
39
  }): Promise<void>;
36
40
  export declare function deleteResponseCache(cache: CacheAdapter, type: PublicQuestionType, context?: {
37
41
  articleSlug?: string;
38
42
  lang?: string;
43
+ queryKey?: string;
39
44
  }): Promise<boolean>;
40
45
  export interface PlaybackChunk {
41
- type: 'thinking' | 'response';
46
+ type: "thinking" | "response";
42
47
  text: string;
43
48
  }
44
- export declare function createResponsePlaybackGenerator(cached: CachedAIResponse, config: Pick<ResponseCacheConfig, 'playbackDelayMs' | 'chunkSize' | 'thinkingPlaybackDelayMs'>): AsyncGenerator<PlaybackChunk, void, unknown>;
49
+ export declare function createResponsePlaybackGenerator(cached: CachedAIResponse, config: Pick<ResponseCacheConfig, "playbackDelayMs" | "chunkSize" | "thinkingPlaybackDelayMs">): AsyncGenerator<PlaybackChunk, void, unknown>;
45
50
  //# sourceMappingURL=response-cache.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"response-cache.d.ts","sourceRoot":"","sources":["../../src/cache/response-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB,EAAE,MAAM,CAAC;CACjC;AAED,eAAO,MAAM,6BAA6B,EAAE,mBAM3C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,mBAAmB,CAyBxF;AAID,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD,MAAM,CAKR;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAIlC;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,kBAAkB,EACxB,IAAI,EAAE,gBAAgB,EACtB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,IAAI,CAAC,mBAAmB,EAAE,iBAAiB,GAAG,WAAW,GAAG,yBAAyB,CAAC,GAC7F,cAAc,CAAC,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,CA4B9C"}
1
+ {"version":3,"file":"response-cache.d.ts","sourceRoot":"","sources":["../../src/cache/response-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAK5D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB,EAAE,MAAM,CAAC;CACjC;AAED,eAAO,MAAM,6BAA6B,EAAE,mBAM3C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvD,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,mBAAmB,CAyBrB;AAID,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE,MAAM,CAMR;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAOlC;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,kBAAkB,EACxB,IAAI,EAAE,gBAAgB,EACtB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACnE,OAAO,CAAC,OAAO,CAAC,CAIlB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,IAAI,CACV,mBAAmB,EACnB,iBAAiB,GAAG,WAAW,GAAG,yBAAyB,CAC5D,GACA,cAAc,CAAC,aAAa,EAAE,IAAI,EAAE,OAAO,CAAC,CA8B9C"}
@@ -1,3 +1,5 @@
1
+ import { createLogger } from "../utils/logger.js";
2
+ const log = createLogger("response-cache");
1
3
  const DEFAULT_RESPONSE_CACHE_CONFIG = {
2
4
  enabled: false,
3
5
  defaultTtl: 3600,
@@ -22,11 +24,11 @@ function getResponseCacheConfig(env) {
22
24
  return defaultVal;
23
25
  };
24
26
  return {
25
- enabled: parseBool(env.AI_RESPONSE_CACHE_ENABLED),
26
- defaultTtl: parseNum(env.AI_RESPONSE_CACHE_TTL, 3600),
27
- playbackDelayMs: parseNum(env.AI_RESPONSE_CACHE_PLAYBACK_DELAY, 20),
28
- chunkSize: parseNum(env.AI_RESPONSE_CACHE_CHUNK_SIZE, 15),
29
- thinkingPlaybackDelayMs: parseNum(env.AI_RESPONSE_CACHE_THINKING_DELAY, 5)
27
+ enabled: parseBool(env.AI_CACHE_ENABLED),
28
+ defaultTtl: parseNum(env.AI_CACHE_TTL, 3600),
29
+ playbackDelayMs: parseNum(env.AI_CACHE_PLAYBACK_DELAY, 20),
30
+ chunkSize: parseNum(env.AI_CACHE_CHUNK_SIZE, 15),
31
+ thinkingPlaybackDelayMs: parseNum(env.AI_CACHE_THINKING_DELAY, 5)
30
32
  };
31
33
  }
32
34
  const RESPONSE_CACHE_PREFIX = "response";
@@ -34,19 +36,27 @@ function buildResponseCacheKey(type, context) {
34
36
  const parts = [RESPONSE_CACHE_PREFIX, type];
35
37
  if (context?.articleSlug) parts.push(context.articleSlug);
36
38
  if (context?.lang) parts.push(context.lang);
39
+ if (context?.queryKey) parts.push(context.queryKey);
37
40
  return parts.join(":");
38
41
  }
39
42
  async function getResponseCache(cache, type, context) {
40
43
  const key = buildResponseCacheKey(type, context);
41
44
  const entry = await cache.get(key);
45
+ log.debug(
46
+ `getResponseCache: key=${key}, hit=${Boolean(entry?.value)}, adapter=${cache.name}`
47
+ );
42
48
  return entry?.value ?? null;
43
49
  }
44
50
  async function setResponseCache(cache, type, data, ttl, context) {
45
51
  const key = buildResponseCacheKey(type, context);
46
52
  await cache.set(key, data, { ttl });
53
+ log.debug(
54
+ `setResponseCache: key=${key}, articles=${data.articles.length}, projects=${data.projects.length}, sources=${data.sources.length}, ttl=${ttl}, adapter=${cache.name}`
55
+ );
47
56
  }
48
57
  async function deleteResponseCache(cache, type, context) {
49
58
  const key = buildResponseCacheKey(type, context);
59
+ log.debug(`deleteResponseCache: key=${key}, adapter=${cache.name}`);
50
60
  return cache.delete(key);
51
61
  }
52
62
  function createResponsePlaybackGenerator(cached, config) {
@@ -59,7 +69,9 @@ function createResponsePlaybackGenerator(cached, config) {
59
69
  if (chunk) {
60
70
  yield { type: "thinking", text: chunk };
61
71
  if (i < thinkingChunks - 1 && thinkingPlaybackDelayMs > 0) {
62
- await new Promise((resolve) => setTimeout(resolve, thinkingPlaybackDelayMs));
72
+ await new Promise(
73
+ (resolve) => setTimeout(resolve, thinkingPlaybackDelayMs)
74
+ );
63
75
  }
64
76
  }
65
77
  }