@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.
- package/README.md +108 -18
- package/dist/cache/global-cache.d.ts +6 -2
- package/dist/cache/global-cache.d.ts.map +1 -1
- package/dist/cache/global-cache.js +24 -9
- package/dist/cache/index.d.ts +7 -6
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +12 -4
- package/dist/cache/injection-cache.d.ts +36 -0
- package/dist/cache/injection-cache.d.ts.map +1 -0
- package/dist/cache/injection-cache.js +90 -0
- package/dist/cache/kv-adapter.d.ts.map +1 -1
- package/dist/cache/kv-adapter.js +2 -1
- package/dist/cache/memory-adapter.d.ts.map +1 -1
- package/dist/cache/memory-adapter.js +2 -1
- package/dist/cache/response-cache.d.ts +10 -5
- package/dist/cache/response-cache.d.ts.map +1 -1
- package/dist/cache/response-cache.js +18 -6
- package/dist/components/AIChatContainer.d.ts +2 -2
- package/dist/components/AIChatContainer.d.ts.map +1 -1
- package/dist/components/AIChatContainer.js +8 -920
- package/dist/components/ChatInput.d.ts +15 -0
- package/dist/components/ChatInput.d.ts.map +1 -0
- package/dist/components/ChatInput.js +72 -0
- package/dist/components/ChatPanel.d.ts +1 -1
- package/dist/components/ChatPanel.d.ts.map +1 -1
- package/dist/components/ChatPanel.js +210 -672
- package/dist/components/CodeBlock.d.ts +31 -0
- package/dist/components/CodeBlock.d.ts.map +1 -0
- package/dist/components/CodeBlock.js +143 -0
- package/dist/components/MarkmapBlock.d.ts +4 -0
- package/dist/components/MarkmapBlock.d.ts.map +1 -0
- package/dist/components/MarkmapBlock.js +180 -0
- package/dist/components/MermaidBlock.d.ts +4 -0
- package/dist/components/MermaidBlock.d.ts.map +1 -0
- package/dist/components/MermaidBlock.js +193 -0
- package/dist/components/MessageBubble.d.ts +21 -0
- package/dist/components/MessageBubble.d.ts.map +1 -0
- package/dist/components/MessageBubble.js +233 -0
- package/dist/components/ReasoningBlock.d.ts +6 -0
- package/dist/components/ReasoningBlock.d.ts.map +1 -0
- package/dist/components/ReasoningBlock.js +11 -0
- package/dist/components/RichText.d.ts +41 -0
- package/dist/components/RichText.d.ts.map +1 -0
- package/dist/components/RichText.js +202 -0
- package/dist/components/VizShared.d.ts +57 -0
- package/dist/components/VizShared.d.ts.map +1 -0
- package/dist/components/VizShared.js +233 -0
- package/dist/components/tool-auto-continue.d.ts +5 -0
- package/dist/components/tool-auto-continue.d.ts.map +1 -0
- package/dist/components/tool-auto-continue.js +33 -0
- package/dist/constants.d.ts +61 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +72 -0
- package/dist/data/index.d.ts +4 -3
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +4 -10
- package/dist/data/knowledge-types.d.ts +8 -0
- package/dist/data/knowledge-types.d.ts.map +1 -0
- package/dist/data/knowledge-types.js +14 -0
- package/dist/data/metadata-loader.d.ts +4 -28
- package/dist/data/metadata-loader.d.ts.map +1 -1
- package/dist/data/metadata-loader.js +11 -34
- package/dist/data/types.d.ts +17 -2
- package/dist/data/types.d.ts.map +1 -1
- package/dist/extensions/index.d.ts +5 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +24 -0
- package/dist/extensions/injector.d.ts +14 -0
- package/dist/extensions/injector.d.ts.map +1 -0
- package/dist/extensions/injector.js +146 -0
- package/dist/extensions/loader.d.ts +5 -0
- package/dist/extensions/loader.d.ts.map +1 -0
- package/dist/extensions/loader.js +45 -0
- package/dist/extensions/registry.d.ts +4 -0
- package/dist/extensions/registry.d.ts.map +1 -0
- package/dist/extensions/registry.js +144 -0
- package/dist/extensions/types.d.ts +126 -0
- package/dist/extensions/types.d.ts.map +1 -0
- package/dist/extensions/types.js +0 -0
- package/dist/fact-registry/prompt-injector.d.ts +1 -1
- package/dist/fact-registry/prompt-injector.d.ts.map +1 -1
- package/dist/fact-registry/prompt-injector.js +2 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/intelligence/citation-guard.d.ts +2 -13
- package/dist/intelligence/citation-guard.d.ts.map +1 -1
- package/dist/intelligence/citation-guard.js +52 -23
- package/dist/intelligence/evidence-analysis.d.ts +24 -16
- package/dist/intelligence/evidence-analysis.d.ts.map +1 -1
- package/dist/intelligence/evidence-analysis.js +118 -20
- package/dist/intelligence/evidence-budget.d.ts +13 -0
- package/dist/intelligence/evidence-budget.d.ts.map +1 -0
- package/dist/intelligence/evidence-budget.js +49 -0
- package/dist/intelligence/index.d.ts +10 -4
- package/dist/intelligence/index.d.ts.map +1 -1
- package/dist/intelligence/index.js +27 -3
- package/dist/intelligence/keyword-extract.d.ts +1 -1
- package/dist/intelligence/keyword-extract.d.ts.map +1 -1
- package/dist/intelligence/keyword-extract.js +5 -9
- package/dist/intelligence/request-interpretation.d.ts +40 -0
- package/dist/intelligence/request-interpretation.d.ts.map +1 -0
- package/dist/intelligence/request-interpretation.js +71 -0
- package/dist/intelligence/response-templates.d.ts +1 -0
- package/dist/intelligence/response-templates.d.ts.map +1 -1
- package/dist/intelligence/response-templates.js +13 -0
- package/dist/prompt/dynamic-layer.d.ts +1 -5
- package/dist/prompt/dynamic-layer.d.ts.map +1 -1
- package/dist/prompt/dynamic-layer.js +145 -9
- package/dist/prompt/prompt-builder.d.ts +1 -1
- package/dist/prompt/prompt-builder.d.ts.map +1 -1
- package/dist/prompt/prompt-builder.js +5 -1
- package/dist/prompt/semi-static-layer.d.ts +1 -1
- package/dist/prompt/semi-static-layer.d.ts.map +1 -1
- package/dist/prompt/semi-static-layer.js +22 -12
- package/dist/prompt/static-layer.d.ts.map +1 -1
- package/dist/prompt/static-layer.js +37 -4
- package/dist/prompt/types.d.ts +9 -4
- package/dist/prompt/types.d.ts.map +1 -1
- package/dist/provider-manager/base.d.ts +5 -1
- package/dist/provider-manager/base.d.ts.map +1 -1
- package/dist/provider-manager/base.js +22 -2
- package/dist/provider-manager/config.d.ts.map +1 -1
- package/dist/provider-manager/config.js +3 -2
- package/dist/provider-manager/index.d.ts +1 -1
- package/dist/provider-manager/index.d.ts.map +1 -1
- package/dist/provider-manager/index.js +1 -2
- package/dist/provider-manager/manager.d.ts +10 -1
- package/dist/provider-manager/manager.d.ts.map +1 -1
- package/dist/provider-manager/manager.js +26 -10
- package/dist/provider-manager/openai.d.ts +2 -2
- package/dist/provider-manager/openai.d.ts.map +1 -1
- package/dist/provider-manager/openai.js +19 -4
- package/dist/provider-manager/types.d.ts +18 -38
- package/dist/provider-manager/types.d.ts.map +1 -1
- package/dist/provider-manager/workers.d.ts +2 -2
- package/dist/provider-manager/workers.d.ts.map +1 -1
- package/dist/provider-manager/workers.js +15 -4
- package/dist/query/followup.d.ts +7 -0
- package/dist/query/followup.d.ts.map +1 -0
- package/dist/query/followup.js +46 -0
- package/dist/query/intent.d.ts +6 -0
- package/dist/query/intent.d.ts.map +1 -0
- package/dist/query/intent.js +137 -0
- package/dist/query/types.d.ts +8 -0
- package/dist/query/types.d.ts.map +1 -0
- package/dist/query/types.js +0 -0
- package/dist/search/hybrid-search.d.ts +111 -0
- package/dist/search/hybrid-search.d.ts.map +1 -0
- package/dist/search/hybrid-search.js +326 -0
- package/dist/search/index.d.ts +11 -9
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +46 -10
- package/dist/search/scoring.d.ts +18 -0
- package/dist/search/scoring.d.ts.map +1 -0
- package/dist/search/{search-utils.js → scoring.js} +14 -27
- package/dist/search/search-api.d.ts +16 -1
- package/dist/search/search-api.d.ts.map +1 -1
- package/dist/search/search-api.js +118 -15
- package/dist/search/search-index.d.ts +2 -2
- package/dist/search/search-index.d.ts.map +1 -1
- package/dist/search/search-index.js +4 -2
- package/dist/search/session-cache.d.ts +4 -10
- package/dist/search/session-cache.d.ts.map +1 -1
- package/dist/search/session-cache.js +12 -45
- package/dist/search/types.d.ts +28 -0
- package/dist/search/types.d.ts.map +1 -1
- package/dist/search/vector-reranker.d.ts +3 -3
- package/dist/search/vector-reranker.d.ts.map +1 -1
- package/dist/search/vector-reranker.js +14 -2
- package/dist/server/chat-handler.d.ts +86 -1
- package/dist/server/chat-handler.d.ts.map +1 -1
- package/dist/server/chat-handler.js +835 -401
- package/dist/server/chat-message-utils.d.ts +6 -0
- package/dist/server/chat-message-utils.d.ts.map +1 -0
- package/dist/server/chat-message-utils.js +40 -0
- package/dist/server/chat-utils.d.ts +30 -0
- package/dist/server/chat-utils.d.ts.map +1 -0
- package/dist/server/chat-utils.js +88 -0
- package/dist/server/dev-server.js +238 -101
- package/dist/server/env-config.d.ts +22 -0
- package/dist/server/env-config.d.ts.map +1 -0
- package/dist/server/env-config.js +25 -0
- package/dist/server/errors.d.ts +1 -0
- package/dist/server/errors.d.ts.map +1 -1
- package/dist/server/errors.js +14 -7
- package/dist/server/index.d.ts +2 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -25
- package/dist/server/metadata-init.d.ts +10 -5
- package/dist/server/metadata-init.d.ts.map +1 -1
- package/dist/server/metadata-init.js +78 -34
- package/dist/server/notify.d.ts +12 -11
- package/dist/server/notify.d.ts.map +1 -1
- package/dist/server/notify.js +46 -48
- package/dist/server/prompt-runtime.d.ts +60 -0
- package/dist/server/prompt-runtime.d.ts.map +1 -0
- package/dist/server/prompt-runtime.js +284 -0
- package/dist/server/stream-helpers.d.ts +30 -16
- package/dist/server/stream-helpers.d.ts.map +1 -1
- package/dist/server/stream-helpers.js +152 -15
- package/dist/server/types.d.ts +47 -12
- package/dist/server/types.d.ts.map +1 -1
- package/dist/structured-output/generator.d.ts +6 -0
- package/dist/structured-output/generator.d.ts.map +1 -0
- package/dist/structured-output/generator.js +164 -0
- package/dist/structured-output/index.d.ts +4 -0
- package/dist/structured-output/index.d.ts.map +1 -0
- package/dist/structured-output/index.js +6 -0
- package/dist/structured-output/schemas/evidence.d.ts +88 -0
- package/dist/structured-output/schemas/evidence.d.ts.map +1 -0
- package/dist/structured-output/schemas/evidence.js +65 -0
- package/dist/structured-output/types.d.ts +69 -0
- package/dist/structured-output/types.d.ts.map +1 -0
- package/dist/structured-output/types.js +0 -0
- package/dist/tools/action-tools.d.ts +63 -0
- package/dist/tools/action-tools.d.ts.map +1 -0
- package/dist/tools/action-tools.js +158 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +30 -0
- package/dist/utils/i18n.d.ts +1 -1
- package/dist/utils/i18n.d.ts.map +1 -1
- package/dist/utils/i18n.js +1 -1
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +36 -0
- package/dist/utils/text.d.ts +11 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +87 -0
- package/dist/utils/url.d.ts +19 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/dist/utils/url.js +13 -0
- package/package.json +46 -12
- package/dist/intelligence/intent-detect.d.ts +0 -40
- package/dist/intelligence/intent-detect.d.ts.map +0 -1
- package/dist/intelligence/intent-detect.js +0 -93
- package/dist/providers/index.d.ts +0 -2
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/index.js +0 -5
- package/dist/search/search-utils.d.ts +0 -47
- package/dist/search/search-utils.d.ts.map +0 -1
- package/dist/stream/index.d.ts +0 -3
- package/dist/stream/index.d.ts.map +0 -1
- package/dist/stream/index.js +0 -8
- package/dist/stream/mock-stream.d.ts +0 -12
- package/dist/stream/mock-stream.d.ts.map +0 -1
- package/dist/stream/mock-stream.js +0 -26
- package/dist/stream/response.d.ts +0 -10
- package/dist/stream/response.d.ts.map +0 -1
- 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/` |
|
|
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
|
|
81
|
-
|
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
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
|
|
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({
|
|
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
|
-
| `./
|
|
223
|
-
| `./
|
|
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
|
|
2
|
-
export type PublicQuestionType =
|
|
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,
|
|
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: [
|
|
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 =
|
|
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
|
|
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
|
};
|
package/dist/cache/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export { MemoryCacheAdapter, type MemoryCacheOptions, } from
|
|
2
|
-
export { KVCacheAdapter, type KVCacheOptions
|
|
3
|
-
export { createCacheKeyBuilder, type CacheAdapter, type CacheEntry, type CacheEntryMetadata, type CacheSetOptions, type CacheGetOptions, type CacheManagerConfig, type CacheEnv, type CachedSearchContext, type CacheResult, type CacheKeyBuilder, } from
|
|
4
|
-
export { detectPublicQuestion, buildGlobalCacheKey, getGlobalSearchCache, setGlobalSearchCache, getGlobalCacheTTL, PUBLIC_QUESTION_PATTERNS, type PublicQuestionType, type PublicQuestionPattern, type DetectedPublicQuestion, } from
|
|
5
|
-
export { getResponseCache, setResponseCache, deleteResponseCache, getResponseCacheConfig, buildResponseCacheKey, createResponsePlaybackGenerator, DEFAULT_RESPONSE_CACHE_CONFIG, type CachedAIResponse, type ResponseCacheConfig, type ResponseCacheEnv, type PlaybackChunk, } from
|
|
6
|
-
|
|
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,
|
|
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"}
|
package/dist/cache/index.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
|
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"}
|
package/dist/cache/kv-adapter.js
CHANGED
|
@@ -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;
|
|
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,12 +1,13 @@
|
|
|
1
|
-
import type { CacheAdapter } from
|
|
2
|
-
import type { ArticleContext, ProjectContext } from
|
|
3
|
-
import type { PublicQuestionType } from
|
|
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:
|
|
46
|
+
type: "thinking" | "response";
|
|
42
47
|
text: string;
|
|
43
48
|
}
|
|
44
|
-
export declare function createResponsePlaybackGenerator(cached: CachedAIResponse, config: Pick<ResponseCacheConfig,
|
|
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,
|
|
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.
|
|
26
|
-
defaultTtl: parseNum(env.
|
|
27
|
-
playbackDelayMs: parseNum(env.
|
|
28
|
-
chunkSize: parseNum(env.
|
|
29
|
-
thinkingPlaybackDelayMs: parseNum(env.
|
|
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(
|
|
72
|
+
await new Promise(
|
|
73
|
+
(resolve) => setTimeout(resolve, thinkingPlaybackDelayMs)
|
|
74
|
+
);
|
|
63
75
|
}
|
|
64
76
|
}
|
|
65
77
|
}
|