@astro-minimax/ai 0.2.0
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 +223 -0
- package/dist/cache/global-cache.d.ts +31 -0
- package/dist/cache/global-cache.d.ts.map +1 -0
- package/dist/cache/global-cache.js +141 -0
- package/dist/cache/index.d.ts +8 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +62 -0
- package/dist/cache/kv-adapter.d.ts +21 -0
- package/dist/cache/kv-adapter.d.ts.map +1 -0
- package/dist/cache/kv-adapter.js +102 -0
- package/dist/cache/memory-adapter.d.ts +24 -0
- package/dist/cache/memory-adapter.d.ts.map +1 -0
- package/dist/cache/memory-adapter.js +95 -0
- package/dist/cache/response-cache.d.ts +45 -0
- package/dist/cache/response-cache.d.ts.map +1 -0
- package/dist/cache/response-cache.js +85 -0
- package/dist/cache/types.d.ts +118 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +16 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.d.ts.map +1 -0
- package/dist/data/index.js +1 -0
- package/dist/data/metadata-loader.d.ts +37 -0
- package/dist/data/metadata-loader.d.ts.map +1 -0
- package/dist/data/metadata-loader.js +54 -0
- package/dist/data/types.d.ts +51 -0
- package/dist/data/types.d.ts.map +1 -0
- package/dist/data/types.js +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/intelligence/citation-guard.d.ts +24 -0
- package/dist/intelligence/citation-guard.d.ts.map +1 -0
- package/dist/intelligence/citation-guard.js +82 -0
- package/dist/intelligence/evidence-analysis.d.ts +29 -0
- package/dist/intelligence/evidence-analysis.d.ts.map +1 -0
- package/dist/intelligence/evidence-analysis.js +88 -0
- package/dist/intelligence/index.d.ts +6 -0
- package/dist/intelligence/index.d.ts.map +1 -0
- package/dist/intelligence/index.js +4 -0
- package/dist/intelligence/intent-detect.d.ts +29 -0
- package/dist/intelligence/intent-detect.d.ts.map +1 -0
- package/dist/intelligence/intent-detect.js +64 -0
- package/dist/intelligence/keyword-extract.d.ts +31 -0
- package/dist/intelligence/keyword-extract.d.ts.map +1 -0
- package/dist/intelligence/keyword-extract.js +114 -0
- package/dist/intelligence/types.d.ts +27 -0
- package/dist/intelligence/types.d.ts.map +1 -0
- package/dist/intelligence/types.js +1 -0
- package/dist/middleware/index.d.ts +3 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +1 -0
- package/dist/middleware/rate-limiter.d.ts +26 -0
- package/dist/middleware/rate-limiter.d.ts.map +1 -0
- package/dist/middleware/rate-limiter.js +129 -0
- package/dist/prompt/dynamic-layer.d.ts +7 -0
- package/dist/prompt/dynamic-layer.d.ts.map +1 -0
- package/dist/prompt/dynamic-layer.js +40 -0
- package/dist/prompt/index.d.ts +6 -0
- package/dist/prompt/index.d.ts.map +1 -0
- package/dist/prompt/index.js +4 -0
- package/dist/prompt/prompt-builder.d.ts +11 -0
- package/dist/prompt/prompt-builder.d.ts.map +1 -0
- package/dist/prompt/prompt-builder.js +19 -0
- package/dist/prompt/semi-static-layer.d.ts +7 -0
- package/dist/prompt/semi-static-layer.d.ts.map +1 -0
- package/dist/prompt/semi-static-layer.js +32 -0
- package/dist/prompt/static-layer.d.ts +3 -0
- package/dist/prompt/static-layer.d.ts.map +1 -0
- package/dist/prompt/static-layer.js +78 -0
- package/dist/prompt/types.d.ts +25 -0
- package/dist/prompt/types.d.ts.map +1 -0
- package/dist/prompt/types.js +1 -0
- package/dist/provider-manager/base.d.ts +26 -0
- package/dist/provider-manager/base.d.ts.map +1 -0
- package/dist/provider-manager/base.js +47 -0
- package/dist/provider-manager/config.d.ts +7 -0
- package/dist/provider-manager/config.d.ts.map +1 -0
- package/dist/provider-manager/config.js +134 -0
- package/dist/provider-manager/index.d.ts +8 -0
- package/dist/provider-manager/index.d.ts.map +1 -0
- package/dist/provider-manager/index.js +6 -0
- package/dist/provider-manager/manager.d.ts +18 -0
- package/dist/provider-manager/manager.d.ts.map +1 -0
- package/dist/provider-manager/manager.js +121 -0
- package/dist/provider-manager/mock.d.ts +18 -0
- package/dist/provider-manager/mock.d.ts.map +1 -0
- package/dist/provider-manager/mock.js +56 -0
- package/dist/provider-manager/openai.d.ts +20 -0
- package/dist/provider-manager/openai.d.ts.map +1 -0
- package/dist/provider-manager/openai.js +83 -0
- package/dist/provider-manager/types.d.ts +217 -0
- package/dist/provider-manager/types.d.ts.map +1 -0
- package/dist/provider-manager/types.js +6 -0
- package/dist/provider-manager/workers.d.ts +20 -0
- package/dist/provider-manager/workers.d.ts.map +1 -0
- package/dist/provider-manager/workers.js +74 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/mock.d.ts +14 -0
- package/dist/providers/mock.d.ts.map +1 -0
- package/dist/providers/mock.js +234 -0
- package/dist/search/index.d.ts +5 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +3 -0
- package/dist/search/search-api.d.ts +28 -0
- package/dist/search/search-api.d.ts.map +1 -0
- package/dist/search/search-api.js +110 -0
- package/dist/search/search-index.d.ts +6 -0
- package/dist/search/search-index.d.ts.map +1 -0
- package/dist/search/search-index.js +22 -0
- package/dist/search/search-utils.d.ts +43 -0
- package/dist/search/search-utils.d.ts.map +1 -0
- package/dist/search/search-utils.js +114 -0
- package/dist/search/session-cache.d.ts +19 -0
- package/dist/search/session-cache.d.ts.map +1 -0
- package/dist/search/session-cache.js +92 -0
- package/dist/search/types.d.ts +41 -0
- package/dist/search/types.d.ts.map +1 -0
- package/dist/search/types.js +1 -0
- package/dist/server/chat-handler.d.ts +3 -0
- package/dist/server/chat-handler.d.ts.map +1 -0
- package/dist/server/chat-handler.js +750 -0
- package/dist/server/dev-server.d.ts +18 -0
- package/dist/server/dev-server.d.ts.map +1 -0
- package/dist/server/dev-server.js +294 -0
- package/dist/server/errors.d.ts +17 -0
- package/dist/server/errors.d.ts.map +1 -0
- package/dist/server/errors.js +41 -0
- package/dist/server/index.d.ts +8 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +5 -0
- package/dist/server/metadata-init.d.ts +11 -0
- package/dist/server/metadata-init.d.ts.map +1 -0
- package/dist/server/metadata-init.js +45 -0
- package/dist/server/notify.d.ts +25 -0
- package/dist/server/notify.d.ts.map +1 -0
- package/dist/server/notify.js +62 -0
- package/dist/server/types.d.ts +56 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +13 -0
- package/dist/stream/index.d.ts +3 -0
- package/dist/stream/index.d.ts.map +1 -0
- package/dist/stream/index.js +2 -0
- package/dist/stream/mock-stream.d.ts +12 -0
- package/dist/stream/mock-stream.d.ts.map +1 -0
- package/dist/stream/mock-stream.js +27 -0
- package/dist/stream/response.d.ts +10 -0
- package/dist/stream/response.d.ts.map +1 -0
- package/dist/stream/response.js +22 -0
- package/dist/utils/i18n.d.ts +18 -0
- package/dist/utils/i18n.d.ts.map +1 -0
- package/dist/utils/i18n.js +148 -0
- package/package.json +93 -0
- package/src/components/AIChatContainer.tsx +30 -0
- package/src/components/AIChatWidget.astro +30 -0
- package/src/components/ChatPanel.tsx +865 -0
- package/src/styles/source.css +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-stream.d.ts","sourceRoot":"","sources":["../../src/stream/mock-stream.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CAAC,MAAM,CAAC,CAoBrF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getMockResponse } from '../providers/mock.js';
|
|
2
|
+
/**
|
|
3
|
+
* Streams a mock response character-by-character as a ReadableStream<string>.
|
|
4
|
+
* Simulates natural typing speed with variable delays.
|
|
5
|
+
*/
|
|
6
|
+
export function streamMockResponse(options) {
|
|
7
|
+
const { question, lang = 'zh', delayRange = [12, 35] } = options;
|
|
8
|
+
const text = getMockResponse(question, lang);
|
|
9
|
+
const [minDelay, maxDelay] = delayRange;
|
|
10
|
+
let index = 0;
|
|
11
|
+
return new ReadableStream({
|
|
12
|
+
async pull(controller) {
|
|
13
|
+
if (index >= text.length) {
|
|
14
|
+
controller.close();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const chunkSize = Math.random() < 0.25 ? 2 : 1;
|
|
18
|
+
const chunk = text.slice(index, index + chunkSize);
|
|
19
|
+
index += chunkSize;
|
|
20
|
+
controller.enqueue(chunk);
|
|
21
|
+
await sleep(minDelay + Math.random() * (maxDelay - minDelay));
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function sleep(ms) {
|
|
26
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common CORS + SSE headers for chat API responses.
|
|
3
|
+
*/
|
|
4
|
+
export declare const STREAM_HEADERS: HeadersInit;
|
|
5
|
+
export declare const JSON_HEADERS: HeadersInit;
|
|
6
|
+
/**
|
|
7
|
+
* Creates a standard error JSON response.
|
|
8
|
+
*/
|
|
9
|
+
export declare function errorResponse(message: string, status?: number): Response;
|
|
10
|
+
//# sourceMappingURL=response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/stream/response.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,WAK5B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,WAG1B,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAM,GAAG,QAAQ,CAKrE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common CORS + SSE headers for chat API responses.
|
|
3
|
+
*/
|
|
4
|
+
export const STREAM_HEADERS = {
|
|
5
|
+
'Content-Type': 'text/event-stream',
|
|
6
|
+
'Cache-Control': 'no-cache',
|
|
7
|
+
'Connection': 'keep-alive',
|
|
8
|
+
'Access-Control-Allow-Origin': '*',
|
|
9
|
+
};
|
|
10
|
+
export const JSON_HEADERS = {
|
|
11
|
+
'Content-Type': 'application/json',
|
|
12
|
+
'Access-Control-Allow-Origin': '*',
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Creates a standard error JSON response.
|
|
16
|
+
*/
|
|
17
|
+
export function errorResponse(message, status = 500) {
|
|
18
|
+
return new Response(JSON.stringify({ error: message }), {
|
|
19
|
+
status,
|
|
20
|
+
headers: JSON_HEADERS,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Package Internationalization
|
|
3
|
+
* Follows the same pattern as packages/core/src/utils/i18n.ts
|
|
4
|
+
*/
|
|
5
|
+
export type AITranslationKey = "ai.reasoning.thinking" | "ai.reasoning.viewReasoning" | "ai.reasoning.waiting" | "ai.error.network" | "ai.error.aborted" | "ai.error.rateLimit" | "ai.error.unavailable" | "ai.error.generic" | "ai.error.format" | "ai.error.noOutput" | "ai.placeholder" | "ai.clear" | "ai.clearConversation" | "ai.close" | "ai.closeChat" | "ai.retry" | "ai.status.searching" | "ai.status.generating" | "ai.status.found" | "ai.status.citation" | "ai.status.fallback" | "ai.prompt.techStack" | "ai.prompt.recommend" | "ai.prompt.build" | "ai.prompt.summarize" | "ai.prompt.explain" | "ai.prompt.related" | "ai.welcome.reading" | "ai.welcome.canHelp" | "ai.welcome.greeting" | "ai.welcome.demo" | "ai.welcome.demoHint" | "ai.welcome.demoPrompt" | "ai.header.reading" | "ai.header.mode" | "ai.assistantName" | "ai.status.live" | "ai.error.emptyMessage" | "ai.error.emptyContent" | "ai.error.inputTooLong" | "ai.error.timeout" | "ai.error.rateLimit.burst" | "ai.error.rateLimit.sustained" | "ai.error.rateLimit.daily" | "ai.prompt.section.responsibilities" | "ai.prompt.section.format" | "ai.prompt.section.principles" | "ai.prompt.section.constraints";
|
|
6
|
+
/**
|
|
7
|
+
* Get translation by key.
|
|
8
|
+
* @param key - Translation key (type-safe)
|
|
9
|
+
* @param lang - Language code ('zh' or 'en')
|
|
10
|
+
* @param vars - Optional variables for interpolation (e.g., { count: 5 })
|
|
11
|
+
*/
|
|
12
|
+
export declare function t(key: AITranslationKey, lang?: string, vars?: Record<string, string | number>): string;
|
|
13
|
+
/**
|
|
14
|
+
* Get normalized language code.
|
|
15
|
+
* Returns 'zh' for Chinese, 'en' for everything else.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getLang(lang?: string): string;
|
|
18
|
+
//# sourceMappingURL=i18n.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/utils/i18n.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,gBAAgB,GAExB,uBAAuB,GACvB,4BAA4B,GAC5B,sBAAsB,GAEtB,kBAAkB,GAClB,kBAAkB,GAClB,oBAAoB,GACpB,sBAAsB,GACtB,kBAAkB,GAClB,iBAAiB,GACjB,mBAAmB,GAEnB,gBAAgB,GAChB,UAAU,GACV,sBAAsB,GACtB,UAAU,GACV,cAAc,GACd,UAAU,GACV,qBAAqB,GACrB,sBAAsB,GACtB,iBAAiB,GACjB,oBAAoB,GACpB,oBAAoB,GAEpB,qBAAqB,GACrB,qBAAqB,GACrB,iBAAiB,GACjB,qBAAqB,GACrB,mBAAmB,GACnB,mBAAmB,GAEnB,oBAAoB,GACpB,oBAAoB,GACpB,qBAAqB,GACrB,iBAAiB,GACjB,qBAAqB,GACrB,uBAAuB,GAEvB,mBAAmB,GACnB,gBAAgB,GAEhB,kBAAkB,GAClB,gBAAgB,GAEhB,uBAAuB,GACvB,uBAAuB,GACvB,uBAAuB,GACvB,kBAAkB,GAElB,0BAA0B,GAC1B,8BAA8B,GAC9B,0BAA0B,GAE1B,oCAAoC,GACpC,0BAA0B,GAC1B,8BAA8B,GAC9B,+BAA+B,CAAC;AA2HpC;;;;;GAKG;AACH,wBAAgB,CAAC,CAAC,GAAG,EAAE,gBAAgB,EAAE,IAAI,GAAE,MAAa,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAY5G;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Package Internationalization
|
|
3
|
+
* Follows the same pattern as packages/core/src/utils/i18n.ts
|
|
4
|
+
*/
|
|
5
|
+
const translations = {
|
|
6
|
+
en: {
|
|
7
|
+
// Reasoning UI
|
|
8
|
+
"ai.reasoning.thinking": "Thinking...",
|
|
9
|
+
"ai.reasoning.viewReasoning": "View reasoning",
|
|
10
|
+
"ai.reasoning.waiting": "Waiting for thoughts...",
|
|
11
|
+
// Error messages
|
|
12
|
+
"ai.error.network": "Network connection failed. Please check your connection.",
|
|
13
|
+
"ai.error.aborted": "Request was cancelled.",
|
|
14
|
+
"ai.error.rateLimit": "Too many requests. Please try again later.",
|
|
15
|
+
"ai.error.unavailable": "AI service is temporarily unavailable.",
|
|
16
|
+
"ai.error.generic": "Something went wrong. Please try again later.",
|
|
17
|
+
"ai.error.format": "Invalid request format.",
|
|
18
|
+
// UI labels
|
|
19
|
+
"ai.placeholder": "Ask a question...",
|
|
20
|
+
"ai.clear": "Clear",
|
|
21
|
+
"ai.clearConversation": "Clear conversation",
|
|
22
|
+
"ai.close": "Close",
|
|
23
|
+
"ai.closeChat": "Close chat",
|
|
24
|
+
"ai.retry": "Retry",
|
|
25
|
+
"ai.status.searching": "Searching...",
|
|
26
|
+
"ai.status.generating": "Generating response...",
|
|
27
|
+
"ai.status.found": "Found {count} related items",
|
|
28
|
+
"ai.status.citation": "Answered from public records",
|
|
29
|
+
"ai.status.fallback": "AI service unavailable, using demo mode",
|
|
30
|
+
// Quick prompts
|
|
31
|
+
"ai.prompt.techStack": "What tech stack is used?",
|
|
32
|
+
"ai.prompt.recommend": "Recommend some articles?",
|
|
33
|
+
"ai.prompt.build": "How to build a similar blog?",
|
|
34
|
+
"ai.prompt.summarize": 'Summarize the key points of "{title}"',
|
|
35
|
+
"ai.prompt.explain": 'Explain "{point}"',
|
|
36
|
+
"ai.prompt.related": "What related content should I read next?",
|
|
37
|
+
// Welcome messages
|
|
38
|
+
"ai.welcome.reading": 'I\'m reading "{title}" with you.\nAsk me to summarize, explain a concept, or explore related topics.',
|
|
39
|
+
"ai.welcome.canHelp": "Hi! I'm the blog AI assistant. Ask me anything and I'll help you find related articles.",
|
|
40
|
+
"ai.welcome.greeting": "Hi! I'm the blog AI assistant.",
|
|
41
|
+
"ai.welcome.demo": "I'm running in demo mode. I can recommend blog articles and external resources.",
|
|
42
|
+
"ai.welcome.demoHint": "For full AI features (RAG search), configure AI_BASE_URL and AI_API_KEY.",
|
|
43
|
+
"ai.welcome.demoPrompt": 'Try: "Recommend articles?" or "How to build this blog?"',
|
|
44
|
+
// Header
|
|
45
|
+
"ai.header.reading": "Reading:",
|
|
46
|
+
"ai.header.mode": "Demo",
|
|
47
|
+
// Assistant branding
|
|
48
|
+
"ai.assistantName": "Blog Avatar",
|
|
49
|
+
"ai.status.live": "Live",
|
|
50
|
+
// Additional error messages
|
|
51
|
+
"ai.error.emptyMessage": "Message cannot be empty.",
|
|
52
|
+
"ai.error.emptyContent": "Message content cannot be empty.",
|
|
53
|
+
"ai.error.inputTooLong": "Message too long, max {max} characters.",
|
|
54
|
+
"ai.error.timeout": "Response timeout, please retry or simplify your question.",
|
|
55
|
+
// Rate limit messages
|
|
56
|
+
"ai.error.rateLimit.burst": "Too many requests, please try again later.",
|
|
57
|
+
"ai.error.rateLimit.sustained": "Too many requests, please wait a minute.",
|
|
58
|
+
"ai.error.rateLimit.daily": "Daily limit reached, please come back tomorrow.",
|
|
59
|
+
"ai.error.noOutput": "Sorry, I could not generate a valid response. Please try rephrasing your question.",
|
|
60
|
+
"ai.prompt.section.responsibilities": "Your Responsibilities",
|
|
61
|
+
"ai.prompt.section.format": "Response Format",
|
|
62
|
+
"ai.prompt.section.principles": "Recommendation Principles",
|
|
63
|
+
"ai.prompt.section.constraints": "Constraints",
|
|
64
|
+
},
|
|
65
|
+
zh: {
|
|
66
|
+
// Reasoning UI
|
|
67
|
+
"ai.reasoning.thinking": "思考中...",
|
|
68
|
+
"ai.reasoning.viewReasoning": "查看思考过程",
|
|
69
|
+
"ai.reasoning.waiting": "等待思考...",
|
|
70
|
+
// Error messages
|
|
71
|
+
"ai.error.network": "网络连接失败,请检查网络",
|
|
72
|
+
"ai.error.aborted": "请求已取消",
|
|
73
|
+
"ai.error.rateLimit": "请求太频繁,请稍后再试",
|
|
74
|
+
"ai.error.unavailable": "AI 服务暂时不可用",
|
|
75
|
+
"ai.error.generic": "出了点问题,请稍后再试",
|
|
76
|
+
"ai.error.format": "请求格式错误",
|
|
77
|
+
// UI labels
|
|
78
|
+
"ai.placeholder": "输入你的问题...",
|
|
79
|
+
"ai.clear": "清除",
|
|
80
|
+
"ai.clearConversation": "清除对话",
|
|
81
|
+
"ai.close": "关闭",
|
|
82
|
+
"ai.closeChat": "关闭聊天",
|
|
83
|
+
"ai.retry": "重试",
|
|
84
|
+
"ai.status.searching": "搜索中...",
|
|
85
|
+
"ai.status.generating": "正在生成回答...",
|
|
86
|
+
"ai.status.found": "找到 {count} 篇相关内容",
|
|
87
|
+
"ai.status.citation": "已基于公开记录直接给出回答",
|
|
88
|
+
"ai.status.fallback": "AI 服务不可用,使用演示模式回复",
|
|
89
|
+
// Quick prompts
|
|
90
|
+
"ai.prompt.techStack": "这个博客用了什么技术?",
|
|
91
|
+
"ai.prompt.recommend": "有哪些文章推荐?",
|
|
92
|
+
"ai.prompt.build": "怎么搭建类似的博客?",
|
|
93
|
+
"ai.prompt.summarize": "总结一下《{title}》的核心观点",
|
|
94
|
+
"ai.prompt.explain": "解释一下「{point}」",
|
|
95
|
+
"ai.prompt.related": "这篇文章和哪些内容相关?",
|
|
96
|
+
// Welcome messages
|
|
97
|
+
"ai.welcome.reading": "我在结合《{title}》陪你阅读。\n你可以让我总结这篇文章、解释某个观点,或者顺着这篇文章继续延伸到相关主题。",
|
|
98
|
+
"ai.welcome.canHelp": "你好!我是博客 AI 助手,问我任何关于博客内容的问题,我可以帮你找到相关文章。",
|
|
99
|
+
"ai.welcome.greeting": "你好!我是博客 AI 助手。",
|
|
100
|
+
"ai.welcome.demo": "我目前在 Demo 模式下,可以推荐博客文章和外部资源。",
|
|
101
|
+
"ai.welcome.demoHint": "启用完整 AI 功能(RAG 搜索增强)需要配置 AI_BASE_URL 和 AI_API_KEY 环境变量。",
|
|
102
|
+
"ai.welcome.demoPrompt": "试试:「有哪些文章推荐?」或「怎么搭建类似的博客?」",
|
|
103
|
+
// Header
|
|
104
|
+
"ai.header.reading": "正在阅读:",
|
|
105
|
+
"ai.header.mode": "演示",
|
|
106
|
+
// Assistant branding
|
|
107
|
+
"ai.assistantName": "博客分身",
|
|
108
|
+
"ai.status.live": "在线",
|
|
109
|
+
// Additional error messages
|
|
110
|
+
"ai.error.emptyMessage": "消息不能为空。",
|
|
111
|
+
"ai.error.emptyContent": "消息内容不能为空。",
|
|
112
|
+
"ai.error.inputTooLong": "消息过长,最多 {max} 字。",
|
|
113
|
+
"ai.error.timeout": "响应超时,请重试或简化问题。",
|
|
114
|
+
// Rate limit messages
|
|
115
|
+
"ai.error.rateLimit.burst": "请求太频繁,请稍后再试。",
|
|
116
|
+
"ai.error.rateLimit.sustained": "请求次数过多,请一分钟后再试。",
|
|
117
|
+
"ai.error.rateLimit.daily": "今日对话次数已达上限,请明天再来。",
|
|
118
|
+
"ai.error.noOutput": "抱歉,我无法生成有效的回答。请尝试换一种方式提问。",
|
|
119
|
+
"ai.prompt.section.responsibilities": "你的职责",
|
|
120
|
+
"ai.prompt.section.format": "回答格式",
|
|
121
|
+
"ai.prompt.section.principles": "推荐原则",
|
|
122
|
+
"ai.prompt.section.constraints": "约束",
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Get translation by key.
|
|
127
|
+
* @param key - Translation key (type-safe)
|
|
128
|
+
* @param lang - Language code ('zh' or 'en')
|
|
129
|
+
* @param vars - Optional variables for interpolation (e.g., { count: 5 })
|
|
130
|
+
*/
|
|
131
|
+
export function t(key, lang = 'zh', vars) {
|
|
132
|
+
const l = lang === 'zh' ? 'zh' : 'en';
|
|
133
|
+
let text = translations[l]?.[key] ?? translations['en'][key] ?? key;
|
|
134
|
+
// Interpolate variables like {count}, {title}, etc.
|
|
135
|
+
if (vars) {
|
|
136
|
+
for (const [k, v] of Object.entries(vars)) {
|
|
137
|
+
text = text.replace(new RegExp(`\\{${k}\\}`, 'g'), String(v));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return text;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get normalized language code.
|
|
144
|
+
* Returns 'zh' for Chinese, 'en' for everything else.
|
|
145
|
+
*/
|
|
146
|
+
export function getLang(lang) {
|
|
147
|
+
return lang === 'zh' ? 'zh' : 'en';
|
|
148
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@astro-minimax/ai",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Vendor-agnostic AI integration package with full RAG pipeline for astro-minimax blogs.",
|
|
6
|
+
"author": "Souloss",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"bin": {
|
|
9
|
+
"astro-ai-dev": "./dist/server/dev-server.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./providers": {
|
|
17
|
+
"types": "./dist/providers/index.d.ts",
|
|
18
|
+
"import": "./dist/providers/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./middleware": {
|
|
21
|
+
"types": "./dist/middleware/index.d.ts",
|
|
22
|
+
"import": "./dist/middleware/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./search": {
|
|
25
|
+
"types": "./dist/search/index.d.ts",
|
|
26
|
+
"import": "./dist/search/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./intelligence": {
|
|
29
|
+
"types": "./dist/intelligence/index.d.ts",
|
|
30
|
+
"import": "./dist/intelligence/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./prompt": {
|
|
33
|
+
"types": "./dist/prompt/index.d.ts",
|
|
34
|
+
"import": "./dist/prompt/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./data": {
|
|
37
|
+
"types": "./dist/data/index.d.ts",
|
|
38
|
+
"import": "./dist/data/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./stream": {
|
|
41
|
+
"types": "./dist/stream/index.d.ts",
|
|
42
|
+
"import": "./dist/stream/index.js"
|
|
43
|
+
},
|
|
44
|
+
"./server": {
|
|
45
|
+
"types": "./dist/server/index.d.ts",
|
|
46
|
+
"import": "./dist/server/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./components/*": "./src/components/*",
|
|
49
|
+
"./styles/source.css": "./src/styles/source.css",
|
|
50
|
+
"./package.json": "./package.json"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"dist/",
|
|
54
|
+
"src/components/",
|
|
55
|
+
"src/styles/",
|
|
56
|
+
"README.md"
|
|
57
|
+
],
|
|
58
|
+
"sideEffects": false,
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsc -p tsconfig.build.json",
|
|
61
|
+
"build:watch": "tsc -p tsconfig.build.json --watch",
|
|
62
|
+
"clean": "rm -rf dist",
|
|
63
|
+
"prepublishOnly": "pnpm run clean && pnpm run build"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@ai-sdk/openai-compatible": "^2.0.35",
|
|
67
|
+
"@astro-minimax/notify": "workspace:*",
|
|
68
|
+
"ai": "^6.0.116",
|
|
69
|
+
"undici": "^6.0.0",
|
|
70
|
+
"workers-ai-provider": "^3.1.2"
|
|
71
|
+
},
|
|
72
|
+
"peerDependencies": {
|
|
73
|
+
"@ai-sdk/react": "^3.0.0",
|
|
74
|
+
"@cloudflare/workers-types": "^4.0.0",
|
|
75
|
+
"preact": "^10.0.0"
|
|
76
|
+
},
|
|
77
|
+
"peerDependenciesMeta": {
|
|
78
|
+
"@ai-sdk/react": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"preact": {
|
|
82
|
+
"optional": true
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"devDependencies": {
|
|
86
|
+
"@cloudflare/workers-types": "^4.0.0",
|
|
87
|
+
"@types/node": "^22.0.0",
|
|
88
|
+
"typescript": "^5.9.3"
|
|
89
|
+
},
|
|
90
|
+
"engines": {
|
|
91
|
+
"node": ">=22.12.0"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useState, useCallback } from 'preact/hooks';
|
|
2
|
+
import { ChatPanel } from './ChatPanel.js';
|
|
3
|
+
import type { AIChatConfig } from './ChatPanel.js';
|
|
4
|
+
import type { ArticleChatContext } from '../server/types.js';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
config: AIChatConfig;
|
|
8
|
+
articleContext?: ArticleChatContext;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function AIChatContainer({ config, articleContext }: Props) {
|
|
12
|
+
const [open, setOpen] = useState(false);
|
|
13
|
+
|
|
14
|
+
const handleToggle = useCallback(() => setOpen(prev => !prev), []);
|
|
15
|
+
const handleClose = useCallback(() => setOpen(false), []);
|
|
16
|
+
|
|
17
|
+
if (typeof window !== 'undefined') {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
(window as any).__aiChatToggle = handleToggle;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<ChatPanel
|
|
24
|
+
open={open}
|
|
25
|
+
onClose={handleClose}
|
|
26
|
+
config={config}
|
|
27
|
+
articleContext={articleContext}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SITE } from "virtual:astro-minimax/config";
|
|
3
|
+
import AIChatContainer from "./AIChatContainer.js";
|
|
4
|
+
import type { ArticleChatContext } from "../server/types.js";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
lang?: string;
|
|
8
|
+
articleContext?: ArticleChatContext;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { lang = SITE.lang ?? "zh", articleContext } = Astro.props;
|
|
12
|
+
const aiEnabled = SITE.ai?.enabled ?? false;
|
|
13
|
+
|
|
14
|
+
const aiConfig = {
|
|
15
|
+
enabled: aiEnabled,
|
|
16
|
+
mockMode: SITE.ai?.mockMode ?? true,
|
|
17
|
+
apiEndpoint: SITE.ai?.apiEndpoint || "/api/chat",
|
|
18
|
+
welcomeMessage: SITE.ai?.welcomeMessage,
|
|
19
|
+
placeholder: SITE.ai?.placeholder,
|
|
20
|
+
lang,
|
|
21
|
+
};
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
{aiEnabled && (
|
|
25
|
+
<AIChatContainer
|
|
26
|
+
client:only="preact"
|
|
27
|
+
config={aiConfig}
|
|
28
|
+
articleContext={articleContext}
|
|
29
|
+
/>
|
|
30
|
+
)}
|