@kpritam/grimoire-output-docusaurus 0.1.8
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/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/internal/assets.d.ts +9 -0
- package/dist/internal/assets.js +50 -0
- package/dist/internal/docusaurusConfig.d.ts +9 -0
- package/dist/internal/docusaurusConfig.js +259 -0
- package/dist/internal/spellbookAssets.d.ts +39 -0
- package/dist/internal/spellbookAssets.js +68 -0
- package/dist/layer.d.ts +3 -0
- package/dist/layer.js +6 -0
- package/dist/shared.d.ts +10 -0
- package/dist/shared.js +36 -0
- package/dist/upstream.d.ts +6 -0
- package/dist/upstream.js +84 -0
- package/package.json +59 -0
- package/src/index.ts +1 -0
- package/src/internal/assets.ts +66 -0
- package/src/internal/docusaurusConfig.ts +281 -0
- package/src/internal/spellbookAssets.ts +80 -0
- package/src/layer.ts +12 -0
- package/src/shared.ts +43 -0
- package/src/upstream.ts +119 -0
- package/templates/spellbook/spellbookPlugin.ts +156 -0
- package/templates/spellbook/src/components/SpellbookChat/ChatEngine.ts +79 -0
- package/templates/spellbook/src/components/SpellbookChat/ChatErrorBoundary.tsx +65 -0
- package/templates/spellbook/src/components/SpellbookChat/Markdown.tsx +259 -0
- package/templates/spellbook/src/components/SpellbookChat/README.md +111 -0
- package/templates/spellbook/src/components/SpellbookChat/SettingsPanel.tsx +376 -0
- package/templates/spellbook/src/components/SpellbookChat/VoiceMode.tsx +867 -0
- package/templates/spellbook/src/components/SpellbookChat/index.tsx +744 -0
- package/templates/spellbook/src/components/SpellbookChat/markdown.module.css +343 -0
- package/templates/spellbook/src/components/SpellbookChat/secretStore.ts +106 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/anthropic.ts +36 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/createCloudProvider.ts +112 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/google.ts +33 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/index.ts +32 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/mapFinishReason.ts +23 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/ollama.ts +44 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/openai.ts +34 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/openaiRealtime.ts +320 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/types.ts +172 -0
- package/templates/spellbook/src/components/SpellbookChat/streamProviders/webllm.ts +214 -0
- package/templates/spellbook/src/components/SpellbookChat/styles.module.css +852 -0
- package/templates/spellbook/src/components/SpellbookChat/systemPrompt.ts +107 -0
- package/templates/spellbook/src/components/SpellbookChat/transformers-ssr-stub.ts +16 -0
- package/templates/spellbook/src/components/SpellbookChat/types.ts +52 -0
- package/templates/spellbook/src/components/SpellbookChat/useBundleLoader.ts +46 -0
- package/templates/spellbook/src/components/SpellbookChat/useChatEngine.ts +524 -0
- package/templates/spellbook/src/components/SpellbookChat/useEmbeddings.ts +147 -0
- package/templates/spellbook/src/components/SpellbookChat/useRetrieval.ts +377 -0
- package/templates/spellbook/src/components/SpellbookChat/useSileroVAD.ts +236 -0
- package/templates/spellbook/src/components/SpellbookChat/useSpeechRecognition.ts +271 -0
- package/templates/spellbook/src/components/SpellbookChat/useSpeechSynthesis.ts +229 -0
- package/templates/spellbook/src/components/SpellbookChat/useUnifiedSTT.ts +134 -0
- package/templates/spellbook/src/components/SpellbookChat/useWhisperSTT.ts +411 -0
- package/templates/spellbook/src/components/SpellbookChat/vad-ssr-stub.ts +25 -0
- package/templates/spellbook/src/components/SpellbookChat/voiceDebug.ts +60 -0
- package/templates/spellbook/src/components/SpellbookChat/voiceFsm.ts +196 -0
- package/templates/spellbook/src/components/SpellbookChat/voiceStyles.module.css +334 -0
- package/templates/spellbook/src/components/SpellbookChat/webllm-ssr-stub.ts +8 -0
- package/templates/spellbook/src/components/SpellbookChatDisabled.tsx +20 -0
- package/templates/spellbook/src/theme/Root.tsx +29 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type { MLCEngine } from "@mlc-ai/web-llm";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
PreloadProgress,
|
|
5
|
+
StreamEvent,
|
|
6
|
+
StreamProvider,
|
|
7
|
+
} from "./types";
|
|
8
|
+
|
|
9
|
+
let enginePromise: Promise<MLCEngine> | null = null;
|
|
10
|
+
let currentModelId: string | null = null;
|
|
11
|
+
|
|
12
|
+
/** Serializes swaps so dispose → create stays ordered across concurrent callers. */
|
|
13
|
+
let swapChain: Promise<unknown> = Promise.resolve();
|
|
14
|
+
|
|
15
|
+
function runExclusive<T>(fn: () => Promise<T>): Promise<T> {
|
|
16
|
+
const next = swapChain.then(fn, fn);
|
|
17
|
+
swapChain = next.then(
|
|
18
|
+
() => undefined,
|
|
19
|
+
() => undefined,
|
|
20
|
+
);
|
|
21
|
+
return next;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function disposeEnginePromise(promise: Promise<MLCEngine>): Promise<void> {
|
|
25
|
+
try {
|
|
26
|
+
const engine = await promise;
|
|
27
|
+
try {
|
|
28
|
+
await engine.unload();
|
|
29
|
+
} catch (err) {
|
|
30
|
+
if (typeof console !== "undefined") {
|
|
31
|
+
console.error("[chat] WebLLM engine unload failed", err);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// Load failed previously; nothing to unload.
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const WEBLLM_MODELS = [
|
|
40
|
+
{
|
|
41
|
+
id: "Llama-3.2-3B-Instruct-q4f16_1-MLC",
|
|
42
|
+
label: "Llama 3.2 3B",
|
|
43
|
+
note: "~1.7 GB",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "Llama-3.2-1B-Instruct-q4f16_1-MLC",
|
|
47
|
+
label: "Llama 3.2 1B",
|
|
48
|
+
note: "~600 MB",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "Phi-3.5-mini-instruct-q4f16_1-MLC",
|
|
52
|
+
label: "Phi-3.5 mini",
|
|
53
|
+
note: "~2 GB",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "Qwen2.5-3B-Instruct-q4f16_1-MLC",
|
|
57
|
+
label: "Qwen 2.5 3B",
|
|
58
|
+
note: "~1.9 GB",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "Qwen2.5-0.5B-Instruct-q4f16_1-MLC",
|
|
62
|
+
label: "Qwen 2.5 0.5B",
|
|
63
|
+
note: "~300 MB",
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
async function getEngine(
|
|
68
|
+
modelId: string,
|
|
69
|
+
onProgress?: (info: PreloadProgress) => void,
|
|
70
|
+
): Promise<MLCEngine> {
|
|
71
|
+
return runExclusive(async () => {
|
|
72
|
+
if (enginePromise && currentModelId === modelId) {
|
|
73
|
+
return enginePromise;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const previous = enginePromise;
|
|
77
|
+
currentModelId = modelId;
|
|
78
|
+
const { CreateMLCEngine } = await import("@mlc-ai/web-llm");
|
|
79
|
+
|
|
80
|
+
if (previous) {
|
|
81
|
+
await disposeEnginePromise(previous);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
enginePromise = CreateMLCEngine(modelId, {
|
|
85
|
+
initProgressCallback: (report: { text: string; progress: number }) => {
|
|
86
|
+
onProgress?.({
|
|
87
|
+
phase: "loading",
|
|
88
|
+
message: report.text,
|
|
89
|
+
fraction: report.progress,
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
return await enginePromise;
|
|
96
|
+
} catch (err) {
|
|
97
|
+
enginePromise = null;
|
|
98
|
+
currentModelId = null;
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Truncate user content if a tiny local model can't realistically swallow it. */
|
|
105
|
+
function clampForLocal(text: string, maxChars: number): string {
|
|
106
|
+
if (text.length <= maxChars) {
|
|
107
|
+
return text;
|
|
108
|
+
}
|
|
109
|
+
return `${text.slice(0, maxChars)}\n\n[... truncated for local model context window]`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const webllmProvider: StreamProvider = {
|
|
113
|
+
id: "webllm",
|
|
114
|
+
displayName: "Local (WebLLM)",
|
|
115
|
+
tagline: "100% in-browser · WebGPU · no key, no network",
|
|
116
|
+
models: WEBLLM_MODELS,
|
|
117
|
+
configFields: [],
|
|
118
|
+
validateConfig: (cfg) => {
|
|
119
|
+
if (!cfg.model?.trim()) {
|
|
120
|
+
return "Pick a model";
|
|
121
|
+
}
|
|
122
|
+
if (typeof navigator !== "undefined" && !("gpu" in navigator)) {
|
|
123
|
+
return "WebGPU not available in this browser (try Chrome 113+ or Safari 18+)";
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
},
|
|
127
|
+
async preload(cfg, onProgress) {
|
|
128
|
+
await getEngine(cfg.model, onProgress);
|
|
129
|
+
},
|
|
130
|
+
async *stream(req, cfg): AsyncIterable<StreamEvent> {
|
|
131
|
+
const engine = await getEngine(cfg.model);
|
|
132
|
+
|
|
133
|
+
const isTinyModel = /\b(0\.5B|1B)\b/i.test(cfg.model);
|
|
134
|
+
const systemBudget = isTinyModel ? 6000 : 24000;
|
|
135
|
+
const userBudget = isTinyModel ? 2000 : 8000;
|
|
136
|
+
|
|
137
|
+
const messages: Array<{ role: "system" | "user" | "assistant"; content: string }> = [];
|
|
138
|
+
if (typeof req.system === "string" && req.system.trim().length > 0) {
|
|
139
|
+
messages.push({
|
|
140
|
+
role: "system",
|
|
141
|
+
content: clampForLocal(req.system, systemBudget),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
for (const m of req.messages) {
|
|
145
|
+
if (!m || typeof m.content !== "string" || m.content.length === 0) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const role = m.role === "assistant" ? "assistant" : "user";
|
|
149
|
+
messages.push({
|
|
150
|
+
role,
|
|
151
|
+
content: clampForLocal(m.content, userBudget),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (messages.length === 0) {
|
|
156
|
+
yield { type: "finish", finishReason: "stop" };
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let stream: AsyncIterable<unknown>;
|
|
161
|
+
try {
|
|
162
|
+
stream = (await engine.chat.completions.create({
|
|
163
|
+
messages,
|
|
164
|
+
stream: true,
|
|
165
|
+
temperature: req.temperature ?? 0.4,
|
|
166
|
+
max_tokens: req.maxTokens ?? 1024,
|
|
167
|
+
})) as AsyncIterable<unknown>;
|
|
168
|
+
} catch (err) {
|
|
169
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
170
|
+
throw new Error(
|
|
171
|
+
msg.includes("WebGPU") || msg.toLowerCase().includes("device")
|
|
172
|
+
? `Local model unavailable: ${msg}`
|
|
173
|
+
: `Local model failed: ${msg}`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let outputTokens = 0;
|
|
178
|
+
try {
|
|
179
|
+
for await (const chunk of stream) {
|
|
180
|
+
if (req.signal?.aborted) {
|
|
181
|
+
yield { type: "finish", finishReason: "abort" };
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (!chunk || typeof chunk !== "object") {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const c = chunk as {
|
|
188
|
+
choices?: ReadonlyArray<{ delta?: { content?: unknown } }>;
|
|
189
|
+
};
|
|
190
|
+
if (!Array.isArray(c.choices) || c.choices.length === 0) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const choice = c.choices[0];
|
|
194
|
+
const content = choice?.delta?.content;
|
|
195
|
+
if (typeof content === "string" && content.length > 0) {
|
|
196
|
+
outputTokens += 1;
|
|
197
|
+
yield { type: "text-delta", text: content };
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} catch (err) {
|
|
201
|
+
if (typeof console !== "undefined") {
|
|
202
|
+
console.error("[chat] WebLLM stream error", err);
|
|
203
|
+
}
|
|
204
|
+
yield { type: "finish", finishReason: "error" };
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
yield {
|
|
209
|
+
type: "finish",
|
|
210
|
+
finishReason: "stop",
|
|
211
|
+
outputTokens: outputTokens || undefined,
|
|
212
|
+
};
|
|
213
|
+
},
|
|
214
|
+
};
|