@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
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { getAuthorContext } from "../data/index.js";
|
|
2
|
+
import { buildSystemPrompt } from "../prompt/index.js";
|
|
3
|
+
import { CHUNK_INJECTION } from "../constants.js";
|
|
4
|
+
import {
|
|
5
|
+
analyzeRetrievedEvidence,
|
|
6
|
+
buildEvidenceSection,
|
|
7
|
+
getCitationGuardPreflight,
|
|
8
|
+
buildUnknownRefusal,
|
|
9
|
+
interpretRequest
|
|
10
|
+
} from "../intelligence/index.js";
|
|
11
|
+
import { matchFactsToQuery, buildFactSection } from "../fact-registry/index.js";
|
|
12
|
+
import {
|
|
13
|
+
resolveVoiceStyleMode,
|
|
14
|
+
buildVoiceStylePrompt,
|
|
15
|
+
mergeFacts
|
|
16
|
+
} from "../extensions/index.js";
|
|
17
|
+
import { buildArticleContextPrompt } from "./chat-utils.js";
|
|
18
|
+
import {
|
|
19
|
+
getArticleChunks
|
|
20
|
+
} from "../search/index.js";
|
|
21
|
+
import { createLogger } from "../utils/logger.js";
|
|
22
|
+
import { extractCodeAnchors } from "../utils/text.js";
|
|
23
|
+
const log = createLogger("prompt-runtime");
|
|
24
|
+
function clipSnippet(text, maxLength = 260) {
|
|
25
|
+
const trimmed = text.trim();
|
|
26
|
+
if (trimmed.length <= maxLength) return trimmed;
|
|
27
|
+
return `${trimmed.slice(0, maxLength).trimEnd()}\u2026`;
|
|
28
|
+
}
|
|
29
|
+
function resolvePromptGuards(args) {
|
|
30
|
+
const { latestText, relatedArticles, relatedProjects, lang } = args;
|
|
31
|
+
const preflight = getCitationGuardPreflight({
|
|
32
|
+
userQuery: latestText,
|
|
33
|
+
articles: relatedArticles,
|
|
34
|
+
projects: relatedProjects,
|
|
35
|
+
lang
|
|
36
|
+
});
|
|
37
|
+
const interpretation = interpretRequest({
|
|
38
|
+
latestText
|
|
39
|
+
});
|
|
40
|
+
const unknownRefusal = interpretation.safety.decision === "refuse" && interpretation.safety.reason === "privacy" ? { text: buildUnknownRefusal(latestText, lang), isUnknown: true } : null;
|
|
41
|
+
return { preflight, unknownRefusal };
|
|
42
|
+
}
|
|
43
|
+
function buildRuntimeSystemPrompt(args) {
|
|
44
|
+
return buildSystemPrompt({
|
|
45
|
+
static: {
|
|
46
|
+
authorName: args.env.SITE_AUTHOR || "\u535A\u4E3B",
|
|
47
|
+
siteUrl: args.env.SITE_URL || "",
|
|
48
|
+
lang: args.lang,
|
|
49
|
+
voiceStylePrompt: args.voiceStylePrompt
|
|
50
|
+
},
|
|
51
|
+
semiStatic: {
|
|
52
|
+
authorContext: getAuthorContext()
|
|
53
|
+
},
|
|
54
|
+
dynamic: {
|
|
55
|
+
userQuery: args.searchQuery,
|
|
56
|
+
articles: args.relatedArticles,
|
|
57
|
+
projects: args.relatedProjects,
|
|
58
|
+
evidenceSection: args.evidenceSection,
|
|
59
|
+
factSection: args.factSection,
|
|
60
|
+
answerMode: args.answerMode,
|
|
61
|
+
lang: args.lang,
|
|
62
|
+
extensions: args.extensions,
|
|
63
|
+
chunksSection: args.chunksSection,
|
|
64
|
+
preferInjectedChunks: args.preferInjectedChunks
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function assemblePromptRuntime(args) {
|
|
69
|
+
const {
|
|
70
|
+
env,
|
|
71
|
+
latestText,
|
|
72
|
+
context,
|
|
73
|
+
lang,
|
|
74
|
+
evidenceAnalysisTimeout,
|
|
75
|
+
timing,
|
|
76
|
+
adapter,
|
|
77
|
+
hasRealProvider,
|
|
78
|
+
extensions,
|
|
79
|
+
cacheKey,
|
|
80
|
+
searchQuery,
|
|
81
|
+
relatedArticles,
|
|
82
|
+
relatedProjects,
|
|
83
|
+
budget,
|
|
84
|
+
answerMode
|
|
85
|
+
} = args;
|
|
86
|
+
let evidenceSection = "";
|
|
87
|
+
let selectedSources = [];
|
|
88
|
+
if (hasRealProvider && adapter) {
|
|
89
|
+
const evidenceStart = Date.now();
|
|
90
|
+
const abortCtrl = new AbortController();
|
|
91
|
+
const timeoutId = setTimeout(
|
|
92
|
+
() => abortCtrl.abort(),
|
|
93
|
+
evidenceAnalysisTimeout
|
|
94
|
+
);
|
|
95
|
+
try {
|
|
96
|
+
const provider = adapter.getProvider();
|
|
97
|
+
const evidenceResult = await analyzeRetrievedEvidence({
|
|
98
|
+
userQuery: latestText,
|
|
99
|
+
articles: relatedArticles,
|
|
100
|
+
projects: relatedProjects,
|
|
101
|
+
provider,
|
|
102
|
+
model: adapter.evidenceModel,
|
|
103
|
+
maxOutputTokens: budget.analysisMaxTokens,
|
|
104
|
+
abortSignal: abortCtrl.signal
|
|
105
|
+
});
|
|
106
|
+
if (evidenceResult.analysis) {
|
|
107
|
+
evidenceSection = buildEvidenceSection(evidenceResult.analysis);
|
|
108
|
+
}
|
|
109
|
+
log.debug(
|
|
110
|
+
`evidenceAnalysis: status=${evidenceResult.parseStatus}, articles=${relatedArticles.length}, projects=${relatedProjects.length}, analysisLength=${typeof evidenceResult.analysis === "string" ? evidenceResult.analysis.length : 0}, usage=${JSON.stringify(evidenceResult.usage ?? null)}`
|
|
111
|
+
);
|
|
112
|
+
timing.evidenceAnalysis = Date.now() - evidenceStart;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
log.debug(
|
|
115
|
+
`evidenceAnalysis: error=${error instanceof Error ? error.message : String(error)}`
|
|
116
|
+
);
|
|
117
|
+
timing.evidenceAnalysis = Date.now() - evidenceStart;
|
|
118
|
+
} finally {
|
|
119
|
+
clearTimeout(timeoutId);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const { preflight, unknownRefusal } = resolvePromptGuards({
|
|
123
|
+
latestText,
|
|
124
|
+
relatedArticles,
|
|
125
|
+
relatedProjects,
|
|
126
|
+
lang
|
|
127
|
+
});
|
|
128
|
+
let matchedFacts = matchFactsToQuery(latestText, lang);
|
|
129
|
+
matchedFacts = mergeFacts(matchedFacts, extensions);
|
|
130
|
+
const factPromptSection = buildFactSection(matchedFacts, lang);
|
|
131
|
+
const articleCategories = relatedArticles.flatMap(
|
|
132
|
+
(a) => a.categories ?? []
|
|
133
|
+
);
|
|
134
|
+
const voiceMode = resolveVoiceStyleMode(
|
|
135
|
+
latestText,
|
|
136
|
+
articleCategories,
|
|
137
|
+
extensions
|
|
138
|
+
);
|
|
139
|
+
const voiceStylePrompt = buildVoiceStylePrompt(voiceMode, extensions);
|
|
140
|
+
const articlePrompt = buildArticleContextPrompt(context);
|
|
141
|
+
let chunksSection = "";
|
|
142
|
+
const articleSlugForChunks = context.scope === "article" && context.article?.slug ? context.article.slug : void 0;
|
|
143
|
+
let currentArticleIdForChunks;
|
|
144
|
+
let articlesWithChunks = relatedArticles.filter(
|
|
145
|
+
(a) => Boolean(a.chunks && a.chunks.length > 0)
|
|
146
|
+
);
|
|
147
|
+
if (articleSlugForChunks) {
|
|
148
|
+
currentArticleIdForChunks = articlesWithChunks.find(
|
|
149
|
+
(article) => article.id === articleSlugForChunks || article.url?.includes(articleSlugForChunks)
|
|
150
|
+
)?.id || (getArticleChunks(articleSlugForChunks) ? articleSlugForChunks : getArticleChunks(`zh/${articleSlugForChunks}`) ? `zh/${articleSlugForChunks}` : getArticleChunks(`en/${articleSlugForChunks}`) ? `en/${articleSlugForChunks}` : void 0);
|
|
151
|
+
const currentArticleId = getArticleChunks(articleSlugForChunks) ? articleSlugForChunks : getArticleChunks(`zh/${articleSlugForChunks}`) ? `zh/${articleSlugForChunks}` : getArticleChunks(`en/${articleSlugForChunks}`) ? `en/${articleSlugForChunks}` : void 0;
|
|
152
|
+
const currentChunks = currentArticleId ? getArticleChunks(currentArticleId) : void 0;
|
|
153
|
+
if (currentChunks?.length) {
|
|
154
|
+
const currentArticleUrl = `${env.SITE_URL ?? ""}/${lang}/posts/${articleSlugForChunks}/`;
|
|
155
|
+
const otherArticles = articlesWithChunks.filter(
|
|
156
|
+
(article) => article.id !== currentArticleId && article.id !== articleSlugForChunks && article.url !== currentArticleUrl && !article.url?.includes(articleSlugForChunks)
|
|
157
|
+
);
|
|
158
|
+
articlesWithChunks = [
|
|
159
|
+
{
|
|
160
|
+
id: currentArticleId,
|
|
161
|
+
title: context.article?.title ?? "",
|
|
162
|
+
url: currentArticleUrl,
|
|
163
|
+
lang,
|
|
164
|
+
keyPoints: context.article?.keyPoints ?? [],
|
|
165
|
+
categories: context.article?.categories ?? [],
|
|
166
|
+
dateTime: 0,
|
|
167
|
+
summary: context.article?.summary,
|
|
168
|
+
chunks: currentChunks
|
|
169
|
+
},
|
|
170
|
+
...otherArticles
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (articlesWithChunks.length > 0) {
|
|
175
|
+
const {
|
|
176
|
+
selectRelevantChunks,
|
|
177
|
+
expandChunkMatchesWithNeighbors,
|
|
178
|
+
formatChunksForInjection
|
|
179
|
+
} = await import("../search/hybrid-search.js");
|
|
180
|
+
const { injectionCache } = await import("../cache/injection-cache.js");
|
|
181
|
+
const maxChunksPerArticle = articleSlugForChunks ? CHUNK_INJECTION.MAX_CHUNKS_PER_ARTICLE * 2 : CHUNK_INJECTION.MAX_CHUNKS_PER_ARTICLE;
|
|
182
|
+
const rawAnchors = extractCodeAnchors(latestText);
|
|
183
|
+
const matchedChunks = selectRelevantChunks(latestText, articlesWithChunks, {
|
|
184
|
+
maxTokens: CHUNK_INJECTION.MAX_TOKENS,
|
|
185
|
+
minChunkScore: CHUNK_INJECTION.MIN_CHUNK_SCORE,
|
|
186
|
+
maxChunksPerArticle,
|
|
187
|
+
rawAnchors,
|
|
188
|
+
currentArticleId: currentArticleIdForChunks
|
|
189
|
+
});
|
|
190
|
+
const effectiveMatches = articleSlugForChunks && latestText.length <= 48 ? expandChunkMatchesWithNeighbors(matchedChunks, {
|
|
191
|
+
includePrevious: true,
|
|
192
|
+
includeNext: true,
|
|
193
|
+
rawAnchors
|
|
194
|
+
}) : matchedChunks;
|
|
195
|
+
const prioritizedMatches = articleSlugForChunks ? [
|
|
196
|
+
...effectiveMatches.filter(
|
|
197
|
+
(match) => match.article.id === articleSlugForChunks || match.article.url?.includes(articleSlugForChunks)
|
|
198
|
+
),
|
|
199
|
+
...effectiveMatches.filter(
|
|
200
|
+
(match) => match.article.id !== articleSlugForChunks && !match.article.url?.includes(articleSlugForChunks)
|
|
201
|
+
)
|
|
202
|
+
] : effectiveMatches;
|
|
203
|
+
if (prioritizedMatches.length > 0) {
|
|
204
|
+
const sessionCacheKey = cacheKey || void 0;
|
|
205
|
+
const newChunks = sessionCacheKey ? injectionCache.filterNewChunks(
|
|
206
|
+
sessionCacheKey,
|
|
207
|
+
prioritizedMatches.map(
|
|
208
|
+
(m) => ({
|
|
209
|
+
id: m.chunk.id,
|
|
210
|
+
content: m.chunk.content
|
|
211
|
+
})
|
|
212
|
+
)
|
|
213
|
+
) : prioritizedMatches.map(
|
|
214
|
+
(m) => ({
|
|
215
|
+
id: m.chunk.id,
|
|
216
|
+
content: m.chunk.content
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
if (newChunks.length > 0) {
|
|
220
|
+
selectedSources = prioritizedMatches.filter(
|
|
221
|
+
(m) => newChunks.some((nc) => nc.id === m.chunk.id)
|
|
222
|
+
).map(
|
|
223
|
+
(m) => ({
|
|
224
|
+
title: m.article.title,
|
|
225
|
+
url: m.article.url,
|
|
226
|
+
lang: m.article.lang,
|
|
227
|
+
reason: "chunk",
|
|
228
|
+
score: m.score,
|
|
229
|
+
chunkId: m.chunk.id,
|
|
230
|
+
heading: m.chunk.heading,
|
|
231
|
+
snippet: clipSnippet(m.chunk.content),
|
|
232
|
+
matchTerms: rawAnchors.filter(
|
|
233
|
+
(anchor) => m.chunk.content.includes(anchor) || m.chunk.heading.includes(anchor)
|
|
234
|
+
)
|
|
235
|
+
})
|
|
236
|
+
);
|
|
237
|
+
chunksSection = formatChunksForInjection(
|
|
238
|
+
prioritizedMatches.filter(
|
|
239
|
+
(m) => newChunks.some((nc) => nc.id === m.chunk.id)
|
|
240
|
+
),
|
|
241
|
+
articleSlugForChunks ? 2200 : 1500
|
|
242
|
+
);
|
|
243
|
+
log.debug(
|
|
244
|
+
`chunkSelection: matched=${matchedChunks.length}, effective=${effectiveMatches.length}, prioritized=${prioritizedMatches.length}, new=${newChunks.length}, selectedSources=${selectedSources.length}`
|
|
245
|
+
);
|
|
246
|
+
if (selectedSources.length > 0) {
|
|
247
|
+
log.debug(
|
|
248
|
+
`chunkSelection top: ${selectedSources.slice(0, 5).map(
|
|
249
|
+
(source) => `${source.title}#${source.chunkId ?? "-"}:${(source.score ?? 0).toFixed(3)}`
|
|
250
|
+
).join(", ")}`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
if (sessionCacheKey) {
|
|
254
|
+
injectionCache.markAsInjected(
|
|
255
|
+
sessionCacheKey,
|
|
256
|
+
newChunks.map((c) => c.id)
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const systemPrompt = buildRuntimeSystemPrompt({
|
|
263
|
+
env,
|
|
264
|
+
lang,
|
|
265
|
+
searchQuery,
|
|
266
|
+
relatedArticles,
|
|
267
|
+
relatedProjects,
|
|
268
|
+
evidenceSection: articlePrompt ? `${evidenceSection}
|
|
269
|
+
${articlePrompt}` : evidenceSection,
|
|
270
|
+
factSection: factPromptSection,
|
|
271
|
+
answerMode,
|
|
272
|
+
extensions,
|
|
273
|
+
cacheKey,
|
|
274
|
+
voiceStylePrompt,
|
|
275
|
+
chunksSection,
|
|
276
|
+
preferInjectedChunks: !!articleSlugForChunks && !!chunksSection
|
|
277
|
+
});
|
|
278
|
+
return { systemPrompt, preflight, unknownRefusal, selectedSources };
|
|
279
|
+
}
|
|
280
|
+
export {
|
|
281
|
+
assemblePromptRuntime,
|
|
282
|
+
buildRuntimeSystemPrompt,
|
|
283
|
+
resolvePromptGuards
|
|
284
|
+
};
|
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { type UIMessage } from 'ai';
|
|
8
|
-
import type { TokenUsage } from '@astro-minimax/notify';
|
|
9
|
-
import type { ProviderAdapter } from '../provider-manager/types.js';
|
|
10
|
-
import type { CachedAIResponse, ResponseCacheConfig } from '../cache/response-cache.js';
|
|
11
|
-
type MessageStreamWriter = {
|
|
12
|
-
write: (part: unknown) => void;
|
|
13
|
-
merge: (stream: ReadableStream) => void;
|
|
14
|
-
};
|
|
1
|
+
import { type UIMessage, type UIMessageStreamWriter, type ToolSet } from "ai";
|
|
2
|
+
import type { NotifyTokenUsage as TokenUsage } from "./types.js";
|
|
3
|
+
import type { ProviderAdapter } from "../provider-manager/types.js";
|
|
4
|
+
import type { CachedAIResponse, ResponseCacheConfig } from "../cache/response-cache.js";
|
|
5
|
+
import type { SourceSelection } from "../search/types.js";
|
|
6
|
+
export type MessageStreamWriter = UIMessageStreamWriter<UIMessage>;
|
|
15
7
|
interface SourceArticle {
|
|
16
8
|
title: string;
|
|
17
9
|
url?: string;
|
|
10
|
+
heading?: string;
|
|
11
|
+
snippet?: string;
|
|
12
|
+
score?: number;
|
|
13
|
+
matchTerms?: string[];
|
|
18
14
|
}
|
|
19
15
|
interface StreamLLMParams {
|
|
20
16
|
writer: MessageStreamWriter;
|
|
@@ -24,6 +20,7 @@ interface StreamLLMParams {
|
|
|
24
20
|
lang: string;
|
|
25
21
|
temperature?: number;
|
|
26
22
|
maxOutputTokens?: number;
|
|
23
|
+
tools?: ToolSet;
|
|
27
24
|
}
|
|
28
25
|
interface StreamLLMResult {
|
|
29
26
|
success: boolean;
|
|
@@ -31,14 +28,31 @@ interface StreamLLMResult {
|
|
|
31
28
|
reasoningText?: string;
|
|
32
29
|
tokenUsage?: TokenUsage;
|
|
33
30
|
generationMs: number;
|
|
31
|
+
hadToolCalls?: boolean;
|
|
32
|
+
}
|
|
33
|
+
interface StreamFailoverParams extends Omit<StreamLLMParams, "adapter"> {
|
|
34
|
+
adapters: ProviderAdapter[];
|
|
35
|
+
}
|
|
36
|
+
interface StreamFailoverResult extends StreamLLMResult {
|
|
37
|
+
adapter: ProviderAdapter | null;
|
|
38
|
+
}
|
|
39
|
+
interface StreamAnswerWithFallbackParams extends Omit<StreamFailoverParams, "adapters"> {
|
|
40
|
+
adapters?: ProviderAdapter[];
|
|
41
|
+
question: string;
|
|
42
|
+
}
|
|
43
|
+
export interface StreamAnswerWithFallbackResult extends StreamFailoverResult {
|
|
44
|
+
usedMockFallback: boolean;
|
|
34
45
|
}
|
|
35
46
|
export declare function writeSearchStatus(writer: MessageStreamWriter, count: number, lang: string): void;
|
|
36
47
|
export declare function writeGeneratingStatus(writer: MessageStreamWriter, lang: string, progress?: number): void;
|
|
37
48
|
export declare function writeDoneStatus(writer: MessageStreamWriter, lang: string): void;
|
|
38
|
-
export declare function writeSourceArticles(writer: MessageStreamWriter, articles: SourceArticle
|
|
49
|
+
export declare function writeSourceArticles(writer: MessageStreamWriter, articles: Array<SourceArticle | SourceSelection>, max?: number): void;
|
|
50
|
+
export declare function writeSourceSnippets(writer: MessageStreamWriter, articles: Array<SourceArticle | SourceSelection>, max?: number): void;
|
|
39
51
|
export declare function writeTextChunk(writer: MessageStreamWriter, text: string, idPrefix?: string): void;
|
|
40
|
-
export declare function writeFinish(writer: MessageStreamWriter, reason?:
|
|
52
|
+
export declare function writeFinish(writer: MessageStreamWriter, reason?: "stop" | "error"): void;
|
|
41
53
|
export declare function streamLLMResponse(params: StreamLLMParams): Promise<StreamLLMResult>;
|
|
54
|
+
export declare function streamLLMWithFailover(params: StreamFailoverParams): Promise<StreamFailoverResult>;
|
|
55
|
+
export declare function streamAnswerWithFallback(params: StreamAnswerWithFallbackParams): Promise<StreamAnswerWithFallbackResult>;
|
|
42
56
|
export declare function streamMockFallback(writer: MessageStreamWriter, question: string, lang: string): Promise<string>;
|
|
43
57
|
export declare function streamCachedResponse(writer: MessageStreamWriter, cachedResponse: CachedAIResponse, config: ResponseCacheConfig, lang: string): Promise<void>;
|
|
44
58
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-helpers.d.ts","sourceRoot":"","sources":["../../src/server/stream-helpers.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"stream-helpers.d.ts","sourceRoot":"","sources":["../../src/server/stream-helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,8 +3,24 @@ import {
|
|
|
3
3
|
convertToModelMessages
|
|
4
4
|
} from "ai";
|
|
5
5
|
import { t } from "../utils/i18n.js";
|
|
6
|
+
import { CHAT_HANDLER } from "../constants.js";
|
|
6
7
|
import { createChatStatusData } from "./types.js";
|
|
7
8
|
import { createResponsePlaybackGenerator } from "../cache/response-cache.js";
|
|
9
|
+
function getStreamResultMetadata(result) {
|
|
10
|
+
return typeof result === "object" && result !== null ? result : {};
|
|
11
|
+
}
|
|
12
|
+
function streamResultHadToolCalls(result) {
|
|
13
|
+
if (typeof result !== "object" || result === null) return false;
|
|
14
|
+
const candidate = result;
|
|
15
|
+
if (Array.isArray(candidate.steps)) {
|
|
16
|
+
for (const step of candidate.steps) {
|
|
17
|
+
if (Array.isArray(step?.toolCalls) && step.toolCalls.length > 0) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return Array.isArray(candidate.toolCalls) && candidate.toolCalls.length > 0;
|
|
23
|
+
}
|
|
8
24
|
function writeSearchStatus(writer, count, lang) {
|
|
9
25
|
writer.write({
|
|
10
26
|
type: "message-metadata",
|
|
@@ -49,6 +65,27 @@ function writeSourceArticles(writer, articles, max = 3) {
|
|
|
49
65
|
}
|
|
50
66
|
}
|
|
51
67
|
}
|
|
68
|
+
function writeSourceSnippets(writer, articles, max = 3) {
|
|
69
|
+
for (const article of articles.slice(0, max)) {
|
|
70
|
+
if (!article.snippet) continue;
|
|
71
|
+
try {
|
|
72
|
+
writer.write({
|
|
73
|
+
type: "data-source-snippet",
|
|
74
|
+
id: `snippet-${article.title}-${article.heading ?? "section"}`,
|
|
75
|
+
data: {
|
|
76
|
+
sourceId: `source-${article.title}`,
|
|
77
|
+
title: article.title,
|
|
78
|
+
url: article.url ?? "#",
|
|
79
|
+
heading: article.heading,
|
|
80
|
+
snippet: article.snippet,
|
|
81
|
+
score: article.score,
|
|
82
|
+
matchTerms: article.matchTerms ?? []
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
52
89
|
function writeTextChunk(writer, text, idPrefix = "text") {
|
|
53
90
|
const id = `${idPrefix}-${Date.now()}`;
|
|
54
91
|
writer.write({ type: "text-start", id });
|
|
@@ -65,18 +102,22 @@ async function streamLLMResponse(params) {
|
|
|
65
102
|
systemPrompt,
|
|
66
103
|
messages,
|
|
67
104
|
lang,
|
|
68
|
-
temperature =
|
|
69
|
-
maxOutputTokens =
|
|
105
|
+
temperature = CHAT_HANDLER.CACHED_REPLAY_TEMPERATURE,
|
|
106
|
+
maxOutputTokens = CHAT_HANDLER.CACHED_REPLAY_MAX_OUTPUT_TOKENS,
|
|
107
|
+
tools
|
|
70
108
|
} = params;
|
|
71
109
|
const start = Date.now();
|
|
72
110
|
try {
|
|
73
111
|
const provider = adapter.getProvider();
|
|
74
112
|
const result = streamText({
|
|
75
|
-
model: provider.chatModel(
|
|
113
|
+
model: provider.chatModel(
|
|
114
|
+
adapter.model
|
|
115
|
+
),
|
|
76
116
|
system: systemPrompt,
|
|
77
117
|
messages: await convertToModelMessages(messages),
|
|
78
118
|
temperature,
|
|
79
119
|
maxOutputTokens,
|
|
120
|
+
tools,
|
|
80
121
|
onError: ({ error }) => {
|
|
81
122
|
console.error("[stream-helpers] streamText error:", error);
|
|
82
123
|
}
|
|
@@ -85,24 +126,29 @@ async function streamLLMResponse(params) {
|
|
|
85
126
|
writer.merge(result.toUIMessageStream({ sendFinish: false }));
|
|
86
127
|
await result.consumeStream({
|
|
87
128
|
onError: (error) => {
|
|
88
|
-
streamErrors.push(
|
|
129
|
+
streamErrors.push(
|
|
130
|
+
error instanceof Error ? error : new Error(String(error))
|
|
131
|
+
);
|
|
89
132
|
}
|
|
90
133
|
});
|
|
91
134
|
const text = await result.text;
|
|
135
|
+
const metadata = getStreamResultMetadata(result);
|
|
136
|
+
const hadToolCalls = streamResultHadToolCalls(result);
|
|
92
137
|
let reasoningText;
|
|
93
|
-
const reasoningPromise =
|
|
138
|
+
const reasoningPromise = metadata.reasoning;
|
|
94
139
|
if (reasoningPromise) {
|
|
95
140
|
try {
|
|
96
141
|
const reasoningOutput = await Promise.resolve(reasoningPromise);
|
|
97
142
|
reasoningText = typeof reasoningOutput === "string" ? reasoningOutput : Array.isArray(reasoningOutput) ? reasoningOutput.map((r) => {
|
|
98
|
-
if (typeof r === "object" && r !== null && "text" in r)
|
|
143
|
+
if (typeof r === "object" && r !== null && "text" in r)
|
|
144
|
+
return r.text;
|
|
99
145
|
return String(r);
|
|
100
146
|
}).join("") : void 0;
|
|
101
147
|
} catch {
|
|
102
148
|
}
|
|
103
149
|
}
|
|
104
150
|
let tokenUsage;
|
|
105
|
-
const usagePromise =
|
|
151
|
+
const usagePromise = metadata.usage;
|
|
106
152
|
if (usagePromise) {
|
|
107
153
|
try {
|
|
108
154
|
const usage = await Promise.resolve(usagePromise);
|
|
@@ -121,22 +167,98 @@ async function streamLLMResponse(params) {
|
|
|
121
167
|
adapter.recordFailure(streamErrors[0]);
|
|
122
168
|
writeTextChunk(writer, t("ai.error.generic", lang), "error");
|
|
123
169
|
writeFinish(writer, "error");
|
|
124
|
-
return {
|
|
170
|
+
return {
|
|
171
|
+
success: false,
|
|
172
|
+
responseText: text,
|
|
173
|
+
reasoningText,
|
|
174
|
+
tokenUsage,
|
|
175
|
+
generationMs,
|
|
176
|
+
hadToolCalls
|
|
177
|
+
};
|
|
125
178
|
}
|
|
126
179
|
if (text.length > 0) {
|
|
127
180
|
adapter.recordSuccess();
|
|
128
181
|
writeFinish(writer);
|
|
129
|
-
return {
|
|
182
|
+
return {
|
|
183
|
+
success: true,
|
|
184
|
+
responseText: text,
|
|
185
|
+
reasoningText,
|
|
186
|
+
tokenUsage,
|
|
187
|
+
generationMs,
|
|
188
|
+
hadToolCalls
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (hadToolCalls) {
|
|
192
|
+
adapter.recordSuccess();
|
|
193
|
+
writeFinish(writer);
|
|
194
|
+
return {
|
|
195
|
+
success: true,
|
|
196
|
+
responseText: "",
|
|
197
|
+
reasoningText,
|
|
198
|
+
tokenUsage,
|
|
199
|
+
generationMs,
|
|
200
|
+
hadToolCalls
|
|
201
|
+
};
|
|
130
202
|
}
|
|
131
203
|
writeTextChunk(writer, t("ai.error.noOutput", lang), "no-output");
|
|
132
204
|
writeFinish(writer);
|
|
133
|
-
return {
|
|
205
|
+
return {
|
|
206
|
+
success: true,
|
|
207
|
+
responseText: "",
|
|
208
|
+
reasoningText,
|
|
209
|
+
tokenUsage,
|
|
210
|
+
generationMs,
|
|
211
|
+
hadToolCalls
|
|
212
|
+
};
|
|
134
213
|
} catch (err) {
|
|
135
214
|
adapter.recordFailure(err instanceof Error ? err : new Error(String(err)));
|
|
136
215
|
console.error("[stream-helpers] Provider threw:", err.message);
|
|
137
|
-
return {
|
|
216
|
+
return {
|
|
217
|
+
success: false,
|
|
218
|
+
responseText: "",
|
|
219
|
+
generationMs: Date.now() - start,
|
|
220
|
+
hadToolCalls: false
|
|
221
|
+
};
|
|
138
222
|
}
|
|
139
223
|
}
|
|
224
|
+
async function streamLLMWithFailover(params) {
|
|
225
|
+
const { adapters, ...streamParams } = params;
|
|
226
|
+
for (const adapter of adapters) {
|
|
227
|
+
const result = await streamLLMResponse({ ...streamParams, adapter });
|
|
228
|
+
if (result.success && (result.responseText.length > 0 || result.hadToolCalls)) {
|
|
229
|
+
return { ...result, adapter };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
success: false,
|
|
234
|
+
responseText: "",
|
|
235
|
+
generationMs: 0,
|
|
236
|
+
adapter: null
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
async function streamAnswerWithFallback(params) {
|
|
240
|
+
const { writer, adapters = [], question, lang, ...streamParams } = params;
|
|
241
|
+
const llmResult = await streamLLMWithFailover({
|
|
242
|
+
writer,
|
|
243
|
+
adapters,
|
|
244
|
+
lang,
|
|
245
|
+
...streamParams
|
|
246
|
+
});
|
|
247
|
+
if (llmResult.adapter) {
|
|
248
|
+
return {
|
|
249
|
+
...llmResult,
|
|
250
|
+
usedMockFallback: false
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const responseText = await streamMockFallback(writer, question, lang);
|
|
254
|
+
return {
|
|
255
|
+
success: true,
|
|
256
|
+
responseText,
|
|
257
|
+
generationMs: llmResult.generationMs,
|
|
258
|
+
adapter: null,
|
|
259
|
+
usedMockFallback: true
|
|
260
|
+
};
|
|
261
|
+
}
|
|
140
262
|
async function streamMockFallback(writer, question, lang) {
|
|
141
263
|
const { getMockResponse } = await import("../providers/mock.js");
|
|
142
264
|
const mockText = getMockResponse(question, lang);
|
|
@@ -153,11 +275,19 @@ async function streamMockFallback(writer, question, lang) {
|
|
|
153
275
|
return mockText;
|
|
154
276
|
}
|
|
155
277
|
async function streamCachedResponse(writer, cachedResponse, config, lang) {
|
|
156
|
-
writeSearchStatus(
|
|
278
|
+
writeSearchStatus(
|
|
279
|
+
writer,
|
|
280
|
+
cachedResponse.articles.length + cachedResponse.projects.length,
|
|
281
|
+
lang
|
|
282
|
+
);
|
|
157
283
|
writeGeneratingStatus(writer, lang);
|
|
158
|
-
writeSourceArticles(writer, cachedResponse.
|
|
284
|
+
writeSourceArticles(writer, cachedResponse.sources);
|
|
285
|
+
writeSourceSnippets(writer, cachedResponse.sources);
|
|
159
286
|
writeGeneratingStatus(writer, lang, 70);
|
|
160
|
-
const playbackGenerator = createResponsePlaybackGenerator(
|
|
287
|
+
const playbackGenerator = createResponsePlaybackGenerator(
|
|
288
|
+
cachedResponse,
|
|
289
|
+
config
|
|
290
|
+
);
|
|
161
291
|
let thinkingId;
|
|
162
292
|
const textId = `text-${Date.now()}`;
|
|
163
293
|
let textStarted = false;
|
|
@@ -167,7 +297,11 @@ async function streamCachedResponse(writer, cachedResponse, config, lang) {
|
|
|
167
297
|
thinkingId = `thinking-${Date.now()}`;
|
|
168
298
|
writer.write({ type: "reasoning-start", id: thinkingId });
|
|
169
299
|
}
|
|
170
|
-
writer.write({
|
|
300
|
+
writer.write({
|
|
301
|
+
type: "reasoning-delta",
|
|
302
|
+
id: thinkingId,
|
|
303
|
+
delta: chunk.text
|
|
304
|
+
});
|
|
171
305
|
} else {
|
|
172
306
|
if (thinkingId) {
|
|
173
307
|
writer.write({ type: "reasoning-end", id: thinkingId });
|
|
@@ -190,13 +324,16 @@ async function streamCachedResponse(writer, cachedResponse, config, lang) {
|
|
|
190
324
|
writeFinish(writer);
|
|
191
325
|
}
|
|
192
326
|
export {
|
|
327
|
+
streamAnswerWithFallback,
|
|
193
328
|
streamCachedResponse,
|
|
194
329
|
streamLLMResponse,
|
|
330
|
+
streamLLMWithFailover,
|
|
195
331
|
streamMockFallback,
|
|
196
332
|
writeDoneStatus,
|
|
197
333
|
writeFinish,
|
|
198
334
|
writeGeneratingStatus,
|
|
199
335
|
writeSearchStatus,
|
|
200
336
|
writeSourceArticles,
|
|
337
|
+
writeSourceSnippets,
|
|
201
338
|
writeTextChunk
|
|
202
339
|
};
|