murmur-rag 0.1.2 → 1.1.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 +23 -19
- package/dist/cdn/anthropic-dc7h9prc.js +2 -0
- package/dist/cdn/chunk-hpx6rpea.js +1 -0
- package/dist/cdn/{embed-hn0r8fmg.js → embed-6gef7ym5.js} +2 -2
- package/dist/cdn/{embed-59r4jck6.js → embed-acjpjh6t.js} +3 -3
- package/dist/cdn/embed-fd96s8qk.js +44 -0
- package/dist/cdn/embed-jdraznwg.js +83 -0
- package/dist/cdn/{embed-q6y6vgv1.js → embed-krrjjyf5.js} +4 -4
- package/dist/cdn/{embed-we24qtes.js → embed-ssqsb38w.js} +2 -2
- package/dist/cdn/embed-z32y952f.js +9 -0
- package/dist/cdn/google-yywkqdph.js +14 -0
- package/dist/cdn/index-bq0x71h1.js +1 -0
- package/dist/cdn/index-gyd02jvt.js +9 -0
- package/dist/cdn/index-jt6n0d28.js +1 -0
- package/dist/cdn/index-nesgnf2n.js +1 -0
- package/dist/cdn/index-wgzgvzaf.js +1 -0
- package/dist/cdn/mistral-0mg4kk7s.js +3 -0
- package/dist/cdn/murmur-j1ky5vmr.js +5 -0
- package/dist/cdn/murmur.js +24 -5
- package/dist/cdn/openai-compatible-zhaj1r43.js +11 -0
- package/dist/cdn/openai-g1685z9v.js +18 -0
- package/dist/cdn/retriever-dqxzjaqk.js +1 -0
- package/dist/cdn/transformers.web-3kmbvcbt.js +46 -0
- package/dist/cli.js +10 -5
- package/dist/lib/chunks/{index-fs6gmhz9.js → index-8h9c8ekq.js} +4 -2
- package/dist/lib/chunks/{index-0x7gav29.js → index-92w507gk.js} +304 -27
- package/dist/lib/chunks/{index-6re28wc1.js → index-b77xqap3.js} +13 -1
- package/dist/lib/chunks/{index-z8pn9sdj.js → index-p86sy3y4.js} +17 -4
- package/dist/lib/chunks/{murmur-wq1d15n7.js → murmur-t36hd4vt.js} +2 -2
- package/dist/lib/chunks/{retriever-1rw4z2n6.js → retriever-wxshtnm0.js} +1 -1
- package/dist/lib/embed.js +4 -2
- package/dist/lib/index.js +5 -3
- package/dist/lib/indexer/index.js +10 -5
- package/dist/lib/local/index.js +163 -0
- package/dist/lib/rag/index.js +13 -11
- package/dist/lib/widget/index.js +2 -2
- package/dist/types/local/index.d.ts +56 -0
- package/dist/types/rag/ground.d.ts +7 -0
- package/dist/types/rag/index.d.ts +1 -1
- package/dist/types/widget/element.d.ts +3 -0
- package/dist/types/widget/i18n.d.ts +14 -0
- package/dist/types/widget/session.d.ts +20 -1
- package/dist/types/widget/styles.d.ts +1 -1
- package/package.json +18 -3
- package/dist/cdn/anthropic-w7nkjk5e.js +0 -2
- package/dist/cdn/chunk-e0j28zmb.js +0 -1
- package/dist/cdn/embed-2xm309sb.js +0 -7
- package/dist/cdn/embed-n7jskdxd.js +0 -44
- package/dist/cdn/embed-sgjh2kg2.js +0 -83
- package/dist/cdn/google-qha5xyx4.js +0 -14
- package/dist/cdn/index-11c9cspb.js +0 -1
- package/dist/cdn/index-e5entetm.js +0 -1
- package/dist/cdn/index-kk9hgxdc.js +0 -1
- package/dist/cdn/index-zrmby2xc.js +0 -9
- package/dist/cdn/mistral-16bekg8b.js +0 -3
- package/dist/cdn/murmur-5vkc01ns.js +0 -5
- package/dist/cdn/openai-compatible-za7hc1fv.js +0 -11
- package/dist/cdn/openai-dhy4fp4c.js +0 -18
- package/dist/cdn/retriever-k9mmdcz5.js +0 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
estimateTokens2
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-b77xqap3.js";
|
|
4
4
|
|
|
5
5
|
// src/rag/retriever.ts
|
|
6
6
|
import MiniSearch from "minisearch";
|
|
7
|
+
var STOPWORDS = new Set(("a an and are as at be but by can do does for from how i if in is it its me my of on or so that the " + "their them then there these this to was what when where which who why will with you your " + "au aux avec ce ces comment dans de des du elle en est et il je la le les leur ma mais me mes " + "mon ne nous on ou où par pas pour qu que quel quelle quels qui sa se ses son sont sur ta te tu " + "un une vos votre vous y").split(" "));
|
|
8
|
+
var MIN_RELATIVE_SCORE = 0.05;
|
|
7
9
|
var fold = (s) => s.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase();
|
|
8
10
|
function passagesOf2(index) {
|
|
9
11
|
return index.chunks.map((c) => {
|
|
@@ -15,15 +17,26 @@ function bm25Retriever2(index) {
|
|
|
15
17
|
const passages = passagesOf2(index);
|
|
16
18
|
const engine = new MiniSearch({
|
|
17
19
|
fields: ["title", "heading", "text"],
|
|
18
|
-
processTerm: (t) =>
|
|
19
|
-
|
|
20
|
+
processTerm: (t) => {
|
|
21
|
+
const term = fold(t);
|
|
22
|
+
return term.length < 2 || STOPWORDS.has(term) ? null : term;
|
|
23
|
+
},
|
|
24
|
+
searchOptions: {
|
|
25
|
+
prefix: (term) => term.length > 3,
|
|
26
|
+
fuzzy: (term) => term.length > 4 ? 0.15 : 0,
|
|
27
|
+
boost: { title: 2, heading: 1.5 }
|
|
28
|
+
}
|
|
20
29
|
});
|
|
21
30
|
engine.addAll(passages.map((p, id) => ({ id, title: p.title, heading: p.heading, text: p.text })));
|
|
22
31
|
return {
|
|
23
32
|
async search(query, { limit = 6, budgetTokens = 3000 } = {}) {
|
|
24
33
|
const out = [];
|
|
25
34
|
let used = 0;
|
|
26
|
-
|
|
35
|
+
const hits = engine.search(query, { combineWith: "OR" });
|
|
36
|
+
const floor = (hits[0]?.score ?? 0) * MIN_RELATIVE_SCORE;
|
|
37
|
+
for (const hit of hits) {
|
|
38
|
+
if (hit.score < floor)
|
|
39
|
+
break;
|
|
27
40
|
const p = passages[Number(hit.id)];
|
|
28
41
|
if (!p)
|
|
29
42
|
continue;
|
package/dist/lib/embed.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__chunks,
|
|
3
3
|
mountMurmur2
|
|
4
|
-
} from "./chunks/index-
|
|
5
|
-
__chunks(import.meta.url,["
|
|
4
|
+
} from "./chunks/index-92w507gk.js";
|
|
5
|
+
__chunks(import.meta.url,["rv6357f7","92w507gk","t36hd4vt","wxshtnm0","dj4321kh","vb9smwta","nb4hdy2t","kaeec41p","b77xqap3","8h9c8ekq","p86sy3y4","1zk822m5"],[["./embed.js",1],["./chunks/index-92w507gk.js",8],["./chunks/murmur-t36hd4vt.js",9,10],["./chunks/retriever-wxshtnm0.js",10],["./indexer/index.js",11],["./llm/index.js"],["./local/index.js"],["./chunks/chunk-kaeec41p.js",11],["./chunks/index-b77xqap3.js"],["./chunks/index-8h9c8ekq.js",10,8],["./chunks/index-p86sy3y4.js",8],["./chunks/index-1zk822m5.js"]],0);
|
|
6
6
|
|
|
7
7
|
// src/embed.ts
|
|
8
8
|
function optionsFromDataset(data, base) {
|
|
@@ -45,6 +45,8 @@ function optionsFromDataset(data, base) {
|
|
|
45
45
|
o.storageKey = data.storageKey;
|
|
46
46
|
if (data.open === "true")
|
|
47
47
|
o.open = true;
|
|
48
|
+
if (data.local === "true")
|
|
49
|
+
o.local = true;
|
|
48
50
|
return o;
|
|
49
51
|
}
|
|
50
52
|
function start() {
|
package/dist/lib/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
passagesOf2,
|
|
3
3
|
bm25Retriever2
|
|
4
|
-
} from "./chunks/index-
|
|
4
|
+
} from "./chunks/index-p86sy3y4.js";
|
|
5
5
|
import {
|
|
6
6
|
searchQuery2,
|
|
7
7
|
createMurmur2
|
|
8
|
-
} from "./chunks/index-
|
|
8
|
+
} from "./chunks/index-8h9c8ekq.js";
|
|
9
9
|
import {
|
|
10
10
|
MAX_CHUNK2,
|
|
11
11
|
splitText2,
|
|
@@ -21,9 +21,10 @@ import {
|
|
|
21
21
|
formatSources2,
|
|
22
22
|
SOURCES_RULE2,
|
|
23
23
|
groundingRules2,
|
|
24
|
+
cleanAnswer2,
|
|
24
25
|
citedSources2,
|
|
25
26
|
passageHref2
|
|
26
|
-
} from "./chunks/index-
|
|
27
|
+
} from "./chunks/index-b77xqap3.js";
|
|
27
28
|
export {
|
|
28
29
|
MAX_CHUNK2 as MAX_CHUNK,
|
|
29
30
|
SOURCES_RULE2 as SOURCES_RULE,
|
|
@@ -31,6 +32,7 @@ export {
|
|
|
31
32
|
buildIndex2 as buildIndex,
|
|
32
33
|
chunkSections2 as chunkSections,
|
|
33
34
|
citedSources2 as citedSources,
|
|
35
|
+
cleanAnswer2 as cleanAnswer,
|
|
34
36
|
createMurmur2 as createMurmur,
|
|
35
37
|
estimateTokens2 as estimateTokens,
|
|
36
38
|
formatSources2 as formatSources,
|
|
@@ -16,7 +16,7 @@ function extractSections(doc, { root = DEFAULT_ROOT, exclude = DEFAULT_EXCLUDE }
|
|
|
16
16
|
return { title, sections: [] };
|
|
17
17
|
const sections = [];
|
|
18
18
|
let cur = { heading: "", anchor: "", text: "" };
|
|
19
|
-
const
|
|
19
|
+
const slugs = new Map;
|
|
20
20
|
const walk = (node) => {
|
|
21
21
|
if (node.nodeType === 3) {
|
|
22
22
|
cur.text += (node.textContent ?? "").replace(/\s+/g, " ");
|
|
@@ -31,16 +31,21 @@ function extractSections(doc, { root = DEFAULT_ROOT, exclude = DEFAULT_EXCLUDE }
|
|
|
31
31
|
if (/^H[1-3]$/.test(tag)) {
|
|
32
32
|
sections.push(cur);
|
|
33
33
|
const heading = (el.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
34
|
-
let anchor = el.id || el.closest("[id]")?.id ||
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
let anchor = el.id || el.closest("[id]")?.id || "";
|
|
35
|
+
if (!anchor) {
|
|
36
|
+
const base = slugify2(heading);
|
|
37
|
+
const n = slugs.get(base) ?? 0;
|
|
38
|
+
slugs.set(base, n + 1);
|
|
39
|
+
anchor = n ? `${base}-${n}` : base;
|
|
40
|
+
}
|
|
38
41
|
cur = { heading, anchor, text: "" };
|
|
39
42
|
return;
|
|
40
43
|
}
|
|
41
44
|
if (tag === "BR")
|
|
42
45
|
cur.text += `
|
|
43
46
|
`;
|
|
47
|
+
if ((tag === "TD" || tag === "TH") && !/(^|\n)\s*$/.test(cur.text))
|
|
48
|
+
cur.text += " | ";
|
|
44
49
|
const block = BLOCK.test(tag);
|
|
45
50
|
if (block)
|
|
46
51
|
cur.text += `
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// src/local/index.ts
|
|
2
|
+
var DEFAULT_LOCAL_MODEL = {
|
|
3
|
+
id: "onnx-community/LFM3-1.7B-ONNX",
|
|
4
|
+
dtype: "q4f16",
|
|
5
|
+
name: "LFM3 1.7B",
|
|
6
|
+
sizeMB: 1090
|
|
7
|
+
};
|
|
8
|
+
async function hasWebGPU() {
|
|
9
|
+
const gpu = globalThis.navigator?.gpu;
|
|
10
|
+
if (!gpu)
|
|
11
|
+
return false;
|
|
12
|
+
try {
|
|
13
|
+
return !!await gpu.requestAdapter();
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function chatMessages(prompt) {
|
|
19
|
+
return prompt.flatMap((m) => {
|
|
20
|
+
if (m.role === "system")
|
|
21
|
+
return [{ role: "system", content: m.content }];
|
|
22
|
+
if (m.role === "tool")
|
|
23
|
+
return [];
|
|
24
|
+
const text = m.content.map((part) => part.type === "text" ? part.text : "").join("").trim();
|
|
25
|
+
return text ? [{ role: m.role, content: text }] : [];
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
var loaded = new Map;
|
|
29
|
+
var listeners = new Map;
|
|
30
|
+
function loadLocalModel(spec = DEFAULT_LOCAL_MODEL, onProgress) {
|
|
31
|
+
const key = `${spec.id}@${spec.dtype}`;
|
|
32
|
+
const set = listeners.get(key) ?? new Set;
|
|
33
|
+
listeners.set(key, set);
|
|
34
|
+
if (onProgress)
|
|
35
|
+
set.add(onProgress);
|
|
36
|
+
let pending = loaded.get(key);
|
|
37
|
+
if (!pending) {
|
|
38
|
+
pending = (async () => {
|
|
39
|
+
if (!await hasWebGPU())
|
|
40
|
+
throw new LocalModelError("webgpu", "This browser has no WebGPU.");
|
|
41
|
+
const { pipeline } = await import("@huggingface/transformers");
|
|
42
|
+
const generator = await pipeline("text-generation", spec.id, {
|
|
43
|
+
dtype: spec.dtype,
|
|
44
|
+
device: "webgpu",
|
|
45
|
+
progress_callback: (info) => {
|
|
46
|
+
if (info.status !== "progress_total")
|
|
47
|
+
return;
|
|
48
|
+
const p = {
|
|
49
|
+
loaded: info.loaded ?? 0,
|
|
50
|
+
total: info.total ?? 0,
|
|
51
|
+
progress: Math.min(1, (info.progress ?? 0) / 100)
|
|
52
|
+
};
|
|
53
|
+
for (const listener of set)
|
|
54
|
+
listener(p);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return generator;
|
|
58
|
+
})();
|
|
59
|
+
pending.catch(() => loaded.delete(key));
|
|
60
|
+
loaded.set(key, pending);
|
|
61
|
+
}
|
|
62
|
+
return pending;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
class LocalModelError extends Error {
|
|
66
|
+
code;
|
|
67
|
+
constructor(code, message) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.code = code;
|
|
70
|
+
this.name = "LocalModelError";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
var usage = (input, output) => ({
|
|
74
|
+
inputTokens: { total: input, noCache: input, cacheRead: undefined, cacheWrite: undefined },
|
|
75
|
+
outputTokens: { total: output, text: output, reasoning: undefined }
|
|
76
|
+
});
|
|
77
|
+
function localModel(options = {}) {
|
|
78
|
+
const spec = options.spec ?? DEFAULT_LOCAL_MODEL;
|
|
79
|
+
async function run(call, onText) {
|
|
80
|
+
const generator = await loadLocalModel(spec, options.onProgress);
|
|
81
|
+
const { TextStreamer, InterruptableStoppingCriteria, StoppingCriteriaList } = await import("@huggingface/transformers");
|
|
82
|
+
const stop = new InterruptableStoppingCriteria;
|
|
83
|
+
const criteria = new StoppingCriteriaList;
|
|
84
|
+
criteria.push(stop);
|
|
85
|
+
const onAbort = () => stop.interrupt();
|
|
86
|
+
call.abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
87
|
+
let tokens = 0;
|
|
88
|
+
const streamer = new TextStreamer(generator.tokenizer, {
|
|
89
|
+
skip_prompt: true,
|
|
90
|
+
skip_special_tokens: true,
|
|
91
|
+
decode_kwargs: { clean_up_tokenization_spaces: false },
|
|
92
|
+
callback_function: (text) => {
|
|
93
|
+
if (text)
|
|
94
|
+
onText(text);
|
|
95
|
+
},
|
|
96
|
+
token_callback_function: () => {
|
|
97
|
+
tokens++;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
try {
|
|
101
|
+
await generator(chatMessages(call.prompt), {
|
|
102
|
+
max_new_tokens: call.maxOutputTokens ?? options.maxNewTokens ?? 512,
|
|
103
|
+
do_sample: true,
|
|
104
|
+
temperature: call.temperature ?? 0.1,
|
|
105
|
+
top_k: 50,
|
|
106
|
+
repetition_penalty: 1.1,
|
|
107
|
+
streamer,
|
|
108
|
+
stopping_criteria: criteria,
|
|
109
|
+
tokenizer_encode_kwargs: { enable_thinking: false }
|
|
110
|
+
});
|
|
111
|
+
} finally {
|
|
112
|
+
call.abortSignal?.removeEventListener("abort", onAbort);
|
|
113
|
+
}
|
|
114
|
+
return { tokens, interrupted: stop.interrupted };
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
specificationVersion: "v4",
|
|
118
|
+
provider: "murmur-local",
|
|
119
|
+
modelId: spec.id,
|
|
120
|
+
supportedUrls: {},
|
|
121
|
+
async doGenerate(call) {
|
|
122
|
+
let text = "";
|
|
123
|
+
const { tokens, interrupted } = await run(call, (t) => {
|
|
124
|
+
text += t;
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
content: [{ type: "text", text }],
|
|
128
|
+
finishReason: { unified: interrupted ? "other" : "stop", raw: undefined },
|
|
129
|
+
usage: usage(0, tokens),
|
|
130
|
+
warnings: []
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
async doStream(call) {
|
|
134
|
+
const stream = new ReadableStream({
|
|
135
|
+
start(controller) {
|
|
136
|
+
controller.enqueue({ type: "stream-start", warnings: [] });
|
|
137
|
+
controller.enqueue({ type: "text-start", id: "0" });
|
|
138
|
+
run(call, (delta) => controller.enqueue({ type: "text-delta", id: "0", delta })).then(({ tokens, interrupted }) => {
|
|
139
|
+
controller.enqueue({ type: "text-end", id: "0" });
|
|
140
|
+
controller.enqueue({
|
|
141
|
+
type: "finish",
|
|
142
|
+
finishReason: { unified: interrupted ? "other" : "stop", raw: undefined },
|
|
143
|
+
usage: usage(0, tokens)
|
|
144
|
+
});
|
|
145
|
+
controller.close();
|
|
146
|
+
}, (error) => {
|
|
147
|
+
controller.enqueue({ type: "error", error });
|
|
148
|
+
controller.close();
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return { stream };
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
export {
|
|
157
|
+
DEFAULT_LOCAL_MODEL,
|
|
158
|
+
LocalModelError,
|
|
159
|
+
chatMessages,
|
|
160
|
+
hasWebGPU,
|
|
161
|
+
loadLocalModel,
|
|
162
|
+
localModel
|
|
163
|
+
};
|
package/dist/lib/rag/index.js
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import"../chunks/index-fp1p1byg.js";
|
|
2
|
-
import {
|
|
3
|
-
MAX_CHUNK2,
|
|
4
|
-
splitText2,
|
|
5
|
-
slugify2,
|
|
6
|
-
chunkSections2,
|
|
7
|
-
markdownSections2,
|
|
8
|
-
buildIndex2,
|
|
9
|
-
isMurmurIndex2
|
|
10
|
-
} from "../chunks/index-1zk822m5.js";
|
|
11
2
|
import {
|
|
12
3
|
passagesOf2,
|
|
13
4
|
bm25Retriever2
|
|
14
|
-
} from "../chunks/index-
|
|
5
|
+
} from "../chunks/index-p86sy3y4.js";
|
|
15
6
|
import {
|
|
16
7
|
estimateTokens2,
|
|
17
8
|
formatSources2,
|
|
18
9
|
SOURCES_RULE2,
|
|
19
10
|
groundingRules2,
|
|
11
|
+
cleanAnswer2,
|
|
20
12
|
citedSources2,
|
|
21
13
|
passageHref2
|
|
22
|
-
} from "../chunks/index-
|
|
14
|
+
} from "../chunks/index-b77xqap3.js";
|
|
15
|
+
import {
|
|
16
|
+
MAX_CHUNK2,
|
|
17
|
+
splitText2,
|
|
18
|
+
slugify2,
|
|
19
|
+
chunkSections2,
|
|
20
|
+
markdownSections2,
|
|
21
|
+
buildIndex2,
|
|
22
|
+
isMurmurIndex2
|
|
23
|
+
} from "../chunks/index-1zk822m5.js";
|
|
23
24
|
export {
|
|
24
25
|
MAX_CHUNK2 as MAX_CHUNK,
|
|
25
26
|
SOURCES_RULE2 as SOURCES_RULE,
|
|
@@ -27,6 +28,7 @@ export {
|
|
|
27
28
|
buildIndex2 as buildIndex,
|
|
28
29
|
chunkSections2 as chunkSections,
|
|
29
30
|
citedSources2 as citedSources,
|
|
31
|
+
cleanAnswer2 as cleanAnswer,
|
|
30
32
|
estimateTokens2 as estimateTokens,
|
|
31
33
|
formatSources2 as formatSources,
|
|
32
34
|
groundingRules2 as groundingRules,
|
package/dist/lib/widget/index.js
CHANGED
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
MurmurChat2,
|
|
9
9
|
defineMurmurChat2,
|
|
10
10
|
mountMurmur2
|
|
11
|
-
} from "../chunks/index-
|
|
12
|
-
__chunks(import.meta.url,["
|
|
11
|
+
} from "../chunks/index-92w507gk.js";
|
|
12
|
+
__chunks(import.meta.url,["f9340r29","92w507gk","t36hd4vt","wxshtnm0","dj4321kh","vb9smwta","nb4hdy2t","kaeec41p","b77xqap3","8h9c8ekq","p86sy3y4","1zk822m5"],[["./index.js",1],["../chunks/index-92w507gk.js",8],["../chunks/murmur-t36hd4vt.js",9,10],["../chunks/retriever-wxshtnm0.js",10],["../indexer/index.js",11],["../llm/index.js"],["../local/index.js"],["../chunks/chunk-kaeec41p.js",11],["../chunks/index-b77xqap3.js"],["../chunks/index-8h9c8ekq.js",10,8],["../chunks/index-p86sy3y4.js",8],["../chunks/index-1zk822m5.js"]],0);
|
|
13
13
|
export {
|
|
14
14
|
MESSAGES2 as MESSAGES,
|
|
15
15
|
MurmurChat2 as MurmurChat,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { LanguageModelV4, LanguageModelV4Prompt } from "@ai-sdk/provider";
|
|
2
|
+
/** A model that runs in the page on WebGPU, through Transformers.js. */
|
|
3
|
+
export interface LocalModelSpec {
|
|
4
|
+
/** Hugging Face repository of the ONNX weights. */
|
|
5
|
+
id: string;
|
|
6
|
+
/** Weights variant: q4f16 is the one meant for WebGPU. */
|
|
7
|
+
dtype: string;
|
|
8
|
+
/** Display name. */
|
|
9
|
+
name: string;
|
|
10
|
+
/** Approximate download, in megabytes, shown before the visitor agrees to it. */
|
|
11
|
+
sizeMB: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* LFM3-1.7B by Liquid AI (October 2026), 4-bit weights with fp16 activations: the smallest
|
|
15
|
+
* text model released after mid-2026 with weights made for WebGPU. About 1.1 GB, downloaded
|
|
16
|
+
* once and kept in the browser's cache.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_LOCAL_MODEL: LocalModelSpec;
|
|
19
|
+
export interface LoadProgress {
|
|
20
|
+
/** Bytes loaded so far, across every file of the model. */
|
|
21
|
+
loaded: number;
|
|
22
|
+
/** Total bytes, once known. */
|
|
23
|
+
total: number;
|
|
24
|
+
/** 0 to 1. */
|
|
25
|
+
progress: number;
|
|
26
|
+
}
|
|
27
|
+
export interface LocalModelOptions {
|
|
28
|
+
/** Which model to run. Default {@link DEFAULT_LOCAL_MODEL}. */
|
|
29
|
+
spec?: LocalModelSpec;
|
|
30
|
+
/** Called while the weights download. */
|
|
31
|
+
onProgress?: (progress: LoadProgress) => void;
|
|
32
|
+
/** Longest answer, in tokens. Default 512. */
|
|
33
|
+
maxNewTokens?: number;
|
|
34
|
+
}
|
|
35
|
+
/** Whether this browser exposes a WebGPU adapter. */
|
|
36
|
+
export declare function hasWebGPU(): Promise<boolean>;
|
|
37
|
+
/** The chat messages a Transformers.js template takes, from an AI SDK prompt (text only). */
|
|
38
|
+
export declare function chatMessages(prompt: LanguageModelV4Prompt): {
|
|
39
|
+
role: string;
|
|
40
|
+
content: string;
|
|
41
|
+
}[];
|
|
42
|
+
type Generator = ((messages: unknown, options: Record<string, unknown>) => Promise<unknown>) & {
|
|
43
|
+
tokenizer: unknown;
|
|
44
|
+
};
|
|
45
|
+
/** Downloads (or reads from cache) and compiles the model; later calls share the same load. */
|
|
46
|
+
export declare function loadLocalModel(spec?: LocalModelSpec, onProgress?: (progress: LoadProgress) => void): Promise<Generator>;
|
|
47
|
+
export declare class LocalModelError extends Error {
|
|
48
|
+
readonly code: "webgpu" | "load";
|
|
49
|
+
constructor(code: "webgpu" | "load", message: string);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* An AI SDK language model that runs in the page: pass it to `createMurmur({ model })`, or let
|
|
53
|
+
* the widget create it with `local: true`. Nothing leaves the browser once the weights are cached.
|
|
54
|
+
*/
|
|
55
|
+
export declare function localModel(options?: LocalModelOptions): LanguageModelV4;
|
|
56
|
+
export {};
|
|
@@ -6,6 +6,13 @@ export declare function formatSources(passages: readonly Passage[]): string;
|
|
|
6
6
|
export declare const SOURCES_RULE: string;
|
|
7
7
|
/** The rules every answer follows; the site owner's prompt comes before them. */
|
|
8
8
|
export declare function groundingRules(language?: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* An answer as a visitor should read it, whatever the model: reasoning blocks and copied
|
|
11
|
+
* source delimiters removed (also half-written ones while streaming), citation variants
|
|
12
|
+
* (`[1, 2]`, `[Source 1]`, `【1】`) written as `[1][2]`, and a trailing "Sources: 1, 2"
|
|
13
|
+
* line dropped, since the interface lists the sources itself.
|
|
14
|
+
*/
|
|
15
|
+
export declare function cleanAnswer(text: string): string;
|
|
9
16
|
/** The source numbers an answer cites, in order of first use, keeping only those that were given. */
|
|
10
17
|
export declare function citedSources(answer: string, count: number): number[];
|
|
11
18
|
/** Link to a passage: its page, at its heading. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { buildIndex, chunkSections, isMurmurIndex, MAX_CHUNK, markdownSections, type Section, slugify, splitText, } from "./chunk.js";
|
|
2
|
-
export { citedSources, estimateTokens, formatSources, groundingRules, passageHref, SOURCES_RULE, } from "./ground.js";
|
|
2
|
+
export { citedSources, cleanAnswer, estimateTokens, formatSources, groundingRules, passageHref, SOURCES_RULE, } from "./ground.js";
|
|
3
3
|
export { bm25Retriever, passagesOf, type Retriever, type SearchOptions } from "./retriever.js";
|
|
4
4
|
export type { Chunk, IndexDoc, MurmurIndex, Passage } from "./types.js";
|
|
@@ -16,6 +16,7 @@ export declare class MurmurChat extends Base {
|
|
|
16
16
|
private options;
|
|
17
17
|
private session;
|
|
18
18
|
private t;
|
|
19
|
+
private uiLang;
|
|
19
20
|
private readonly root;
|
|
20
21
|
private launcher;
|
|
21
22
|
private panel;
|
|
@@ -48,6 +49,8 @@ export declare class MurmurChat extends Base {
|
|
|
48
49
|
/** Draws an answer: its text with citation rings, then the sources it cites. */
|
|
49
50
|
private fillAnswer;
|
|
50
51
|
private submit;
|
|
52
|
+
/** Asks the visitor before downloading the in-browser model, or says why it cannot run. */
|
|
53
|
+
private showConsent;
|
|
51
54
|
/** The visitor's model settings, drawn from llm-connect's store. */
|
|
52
55
|
private showSetup;
|
|
53
56
|
private hideSetup;
|
|
@@ -40,6 +40,20 @@ export interface Messages {
|
|
|
40
40
|
searchModels: string;
|
|
41
41
|
recommended: string;
|
|
42
42
|
otherProviders: string;
|
|
43
|
+
localOption: string;
|
|
44
|
+
localOptionHint: string;
|
|
45
|
+
localActive: string;
|
|
46
|
+
orKey: string;
|
|
47
|
+
useKey: string;
|
|
48
|
+
localConsent: string;
|
|
49
|
+
localDownload: string;
|
|
50
|
+
localLoading: string;
|
|
51
|
+
localLoadingLabel: string;
|
|
52
|
+
localPreparing: string;
|
|
53
|
+
localNoWebGPU: string;
|
|
54
|
+
localLoadFailed: string;
|
|
55
|
+
gigabytes: string;
|
|
56
|
+
megabytes: string;
|
|
43
57
|
}
|
|
44
58
|
export declare const MESSAGES: Record<string, Messages>;
|
|
45
59
|
/** The first supported language among the candidates (`fr-CA` matches `fr`), else English. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { LanguageModel } from "ai";
|
|
2
2
|
import type { Turn } from "../core/murmur.js";
|
|
3
3
|
import type { LlmConfig, LlmSettings, LlmSettingsStore } from "../llm/index.js";
|
|
4
|
+
import type { LoadProgress, LocalModelSpec } from "../local/index.js";
|
|
4
5
|
import type { Retriever } from "../rag/retriever.js";
|
|
5
6
|
import type { MurmurIndex, Passage } from "../rag/types.js";
|
|
6
7
|
import type { Messages } from "./i18n.js";
|
|
@@ -29,13 +30,18 @@ export interface WidgetOptions {
|
|
|
29
30
|
storageKey?: string;
|
|
30
31
|
/** Open the panel on load. */
|
|
31
32
|
open?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Answer with a model that runs in the visitor's browser on WebGPU: `true` for the default
|
|
35
|
+
* (LFM3 1.7B, about 1.1 GB, downloaded once after the visitor agrees), or your own spec.
|
|
36
|
+
*/
|
|
37
|
+
local?: boolean | LocalModelSpec;
|
|
32
38
|
}
|
|
33
39
|
export interface ChatTurn extends Turn {
|
|
34
40
|
passages?: Pick<Passage, "url" | "anchor" | "title" | "heading">[];
|
|
35
41
|
cited?: number[];
|
|
36
42
|
error?: string;
|
|
37
43
|
}
|
|
38
|
-
type Mode = "custom" | "proxy" | "visitor";
|
|
44
|
+
type Mode = "custom" | "local" | "proxy" | "visitor";
|
|
39
45
|
/** Everything the widget does except drawing: index, model settings, history. */
|
|
40
46
|
export declare class ChatSession {
|
|
41
47
|
readonly options: WidgetOptions;
|
|
@@ -43,8 +49,19 @@ export declare class ChatSession {
|
|
|
43
49
|
private engine;
|
|
44
50
|
private storePromise;
|
|
45
51
|
private readonly historyKey;
|
|
52
|
+
private readonly key;
|
|
53
|
+
/** Called while the in-browser model downloads. */
|
|
54
|
+
onProgress: ((progress: LoadProgress) => void) | null;
|
|
46
55
|
constructor(options: WidgetOptions);
|
|
47
56
|
get mode(): Mode;
|
|
57
|
+
/** The in-browser model this session would run. */
|
|
58
|
+
localSpec(): Promise<LocalModelSpec>;
|
|
59
|
+
private get visitorChoseLocal();
|
|
60
|
+
/** The visitor picks the in-browser model (or goes back to a key). */
|
|
61
|
+
useLocal(on: boolean): void;
|
|
62
|
+
/** Whether the visitor must still agree to download the in-browser model. */
|
|
63
|
+
get needsDownloadConsent(): boolean;
|
|
64
|
+
agreeToDownload(): void;
|
|
48
65
|
persist(): void;
|
|
49
66
|
clear(): void;
|
|
50
67
|
/** The visitor's settings form, created on first use. */
|
|
@@ -52,6 +69,8 @@ export declare class ChatSession {
|
|
|
52
69
|
/** The settings to call, or null when the visitor has to set up a model first. */
|
|
53
70
|
settings(): Promise<LlmSettings | null>;
|
|
54
71
|
needsSetup(): Promise<boolean>;
|
|
72
|
+
/** Whether a model can run in this browser at all. */
|
|
73
|
+
canRunLocally(): Promise<boolean>;
|
|
55
74
|
private loadIndex;
|
|
56
75
|
private createEngine;
|
|
57
76
|
/** Asks a question; `onText` receives the answer so far. Resolves to the finished turn. */
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* custom properties on the element: --murmur-accent, --murmur-accent-ink, --murmur-font,
|
|
4
4
|
* --murmur-radius, --murmur-z.
|
|
5
5
|
*/
|
|
6
|
-
export declare const STYLES = "\n:host {\n --accent: var(--murmur-accent, #211c17);\n --accent-ink: var(--murmur-accent-ink, #f6efe0);\n --lamp: var(--murmur-lamp, #e0a032);\n --surface: #fbf8f1;\n --raised: #f2ece0;\n --ink: #211c17;\n --muted: #675c4f;\n --line: #e2d8c5;\n --focus: #b4541d;\n --danger: #a1301f;\n --radius: var(--murmur-radius, 16px);\n --shadow: 0 18px 48px -12px rgb(33 28 23 / 0.28), 0 4px 12px -4px rgb(33 28 23 / 0.16);\n --ease: cubic-bezier(0.16, 1, 0.3, 1);\n all: initial;\n position: fixed;\n z-index: var(--murmur-z, 2147483000);\n inset: auto 20px 20px auto;\n font-family: var(--murmur-font, inherit);\n font-size: 15px;\n line-height: 1.5;\n color: var(--ink);\n color-scheme: light;\n -webkit-font-smoothing: antialiased;\n}\n:host([position=\"bottom-left\"]) { inset: auto auto 20px 20px; }\n:host([inline]) { position: relative; inset: auto; z-index: auto; display: block; height: 100%; }\n:host([theme=\"dark\"]) { --surface: #1c1814; --raised: #26211b; --ink: #f2ead9; --muted: #b8ab95; --line: #3b342b; --focus: #f0a35c; --danger: #f08a73; color-scheme: dark; }\n@media (prefers-color-scheme: dark) {\n :host(:not([theme=\"light\"])) { --surface: #1c1814; --raised: #26211b; --ink: #f2ead9; --muted: #b8ab95; --line: #3b342b; --focus: #f0a35c; --danger: #f08a73; color-scheme: dark; }\n :host(:not([theme=\"light\"])) { --murmur-accent-default: #f2ead9; }\n}\n@media print { :host { display: none !important; } }\n* { box-sizing: border-box; }\n[hidden] { display: none !important; }\n::selection { background: color-mix(in oklab, var(--lamp) 45%, transparent); color: inherit; }\nbutton, input, select, textarea { font: inherit; color: inherit; }\nbutton { cursor: pointer; }\n:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }\na { color: inherit; text-underline-offset: 0.2em; text-decoration-thickness: 1px; }\n\n/* Launcher */\n.launcher {\n display: inline-flex; align-items: center; gap: 10px;\n height: 52px; padding: 0 20px 0 16px; border: 0; border-radius: 26px;\n background: var(--accent); color: var(--accent-ink);\n box-shadow: var(--shadow); font-weight: 600; letter-spacing: 0.01em;\n transition: transform 220ms var(--ease), box-shadow 220ms var(--ease);\n}\n.launcher:hover { transform: translateY(-2px); }\n.launcher:active { transform: translateY(0); }\n.launcher[aria-expanded=\"true\"] { display: none; }\n.lamp {\n width: 8px; height: 8px; border-radius: 50%; flex: none;\n background: var(--lamp); box-shadow: 0 0 0 3px color-mix(in oklab, var(--lamp) 28%, transparent);\n}\n.lamp.off { background: transparent; box-shadow: inset 0 0 0 1.5px var(--muted); }\n.lamp.busy { animation: blink 1.1s steps(2, jump-none) infinite; }\n@keyframes blink { 50% { opacity: 0.25; } }\n\n/* Panel */\n.panel {\n position: absolute; right: 0; bottom: 0;\n display: flex; flex-direction: column;\n width: min(400px, calc(100vw - 40px)); height: min(640px, calc(100dvh - 40px));\n background: var(--surface); border-radius: var(--radius); box-shadow: var(--shadow);\n overflow: hidden; transform-origin: bottom right;\n animation: rise 320ms var(--ease);\n}\n:host([position=\"bottom-left\"]) .panel { right: auto; left: 0; transform-origin: bottom left; }\n:host([inline]) .panel { position: static; width: 100%; height: 100%; box-shadow: none; animation: none; border-radius: inherit; }\n@keyframes rise { from { opacity: 0; transform: translateY(12px) scale(0.98); } }\n@media (max-width: 480px) {\n :host(:not([inline])) { inset: auto 12px 12px auto; }\n :host(:not([inline])[position=\"bottom-left\"]) { inset: auto auto 12px 12px; }\n :host(:not([inline])) .panel { position: fixed; inset: 0; width: 100%; height: 100dvh; border-radius: 0; }\n}\n\nheader {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 10px 14px 18px; background: var(--accent); color: var(--accent-ink);\n}\nheader h2 { flex: 1; margin: 0; font-size: 16px; font-weight: 650; letter-spacing: 0.005em; line-height: 1.25; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.icon-btn {\n display: inline-grid; place-items: center; width: 36px; height: 36px; flex: none;\n border: 0; border-radius: 10px; background: transparent; color: inherit;\n transition: background 160ms;\n}\n.icon-btn:hover { background: color-mix(in oklab, currentColor 14%, transparent); }\nheader .icon-btn:focus-visible { outline-color: var(--accent-ink); }\n\n/* Conversation */\n.log {\n flex: 1; overflow-y: auto; overscroll-behavior: contain;\n padding: 18px 18px 8px; display: flex; flex-direction: column; gap: 16px;\n scrollbar-width: thin; scrollbar-color: var(--line) transparent;\n}\n.msg { max-width: 100%; overflow-wrap: anywhere; }\n.msg p { margin: 0 0 0.6em; }\n.msg p:last-child { margin-bottom: 0; }\n.msg ul, .msg ol { margin: 0 0 0.6em; padding-left: 1.3em; }\n.msg li + li { margin-top: 0.2em; }\n.msg code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.88em; background: var(--raised); padding: 0.1em 0.35em; border-radius: 5px; }\n.msg pre { margin: 0 0 0.6em; padding: 10px 12px; background: var(--raised); border-radius: 10px; overflow-x: auto; }\n.msg pre code { background: none; padding: 0; }\n.user {\n align-self: flex-end; max-width: 85%;\n padding: 9px 14px; border-radius: 16px 16px 4px 16px;\n background: var(--accent); color: var(--accent-ink); white-space: pre-wrap;\n}\n.bot { align-self: stretch; }\n.greeting { color: var(--muted); }\n\n.cite {\n display: inline-grid; place-items: center; min-width: 1.45em; height: 1.45em; padding: 0 0.3em;\n margin: 0 0.1em; vertical-align: 0.12em;\n border-radius: 1em; box-shadow: inset 0 0 0 1.25px currentColor;\n font-size: 0.72em; font-weight: 700; line-height: 1; text-decoration: none;\n font-variant-numeric: tabular-nums; color: var(--muted);\n transition: background 160ms, color 160ms;\n}\n.cite:hover, .cite:focus-visible { background: var(--lamp); color: #211c17; box-shadow: none; }\n\n.sources { margin: 12px 0 0; padding: 0; list-style: none; display: grid; gap: 2px; }\n.sources-label { margin: 18px 0 2px; font-size: 12px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }\n.sources a {\n display: grid; grid-template-columns: auto 1fr; align-items: baseline; gap: 10px;\n padding: 7px 10px; margin: 0 -10px; border-radius: 10px; text-decoration: none;\n}\n.sources a:hover { background: var(--raised); }\n.sources .cite { margin: 0; }\n.sources .where { min-width: 0; font-size: 14px; line-height: 1.35; }\n.sources .where span { color: var(--muted); }\n\n.status { display: flex; align-items: center; gap: 10px; color: var(--muted); font-size: 14px; }\n.error { color: var(--danger); }\n.error p { margin: 0 0 8px; }\n.text-btn {\n display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 9px;\n border: 0; background: var(--raised); color: var(--ink); font-size: 14px; font-weight: 600;\n}\n.text-btn:hover { background: var(--line); }\n\n/* Composer */\nform.composer {\n display: flex; align-items: flex-end; gap: 8px; margin: 8px 12px 0; padding: 6px 6px 6px 14px;\n border-radius: 14px; background: var(--raised);\n box-shadow: inset 0 0 0 1px var(--line); transition: box-shadow 160ms;\n}\nform.composer:focus-within { box-shadow: inset 0 0 0 1.5px var(--focus); }\ntextarea {\n flex: 1; min-height: 38px; max-height: 140px; padding: 8px 0; resize: none;\n border: 0; background: transparent; outline: none; line-height: 1.4;\n}\ntextarea:focus-visible { outline: none; }\ntextarea::placeholder, input::placeholder { color: var(--muted); opacity: 1; }\n.send {\n display: inline-grid; place-items: center; width: 38px; height: 38px; flex: none;\n border: 0; border-radius: 10px; background: var(--accent); color: var(--accent-ink);\n transition: opacity 160ms;\n}\n.send:disabled { opacity: 0.35; cursor: default; }\nfooter { padding: 8px 18px 12px; font-size: 12px; color: var(--muted); text-align: center; }\nfooter a { text-decoration: none; }\nfooter a:hover { text-decoration: underline; }\n\n/* Settings */\n.setup { flex: 1; overflow-y: auto; padding: 18px; display: grid; gap: 16px; align-content: start; }\n.setup p.intro { margin: 0; color: var(--muted); font-size: 14px; }\n.field { display: grid; gap: 6px; }\n.field > span { font-size: 13px; font-weight: 650; }\n.field small { color: var(--muted); font-size: 12.5px; }\n.field input:not([type=\"checkbox\"]), .field select {\n width: 100%; height: 40px; padding: 0 12px; border-radius: 10px;\n border: 0; background: var(--raised); box-shadow: inset 0 0 0 1px var(--line);\n}\n.field input:focus-visible, .field select:focus-visible { outline: none; box-shadow: inset 0 0 0 1.5px var(--focus); }\n.check { display: flex; align-items: center; gap: 10px; font-size: 14px; }\n.check input { width: 18px; height: 18px; accent-color: var(--accent); margin: 0; }\n.presets { display: flex; gap: 8px; flex-wrap: wrap; }\n.row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }\n.primary {\n display: inline-flex; align-items: center; justify-content: center; gap: 8px;\n height: 42px; padding: 0 18px; border: 0; border-radius: 11px;\n background: var(--accent); color: var(--accent-ink); font-weight: 650;\n}\n.primary:disabled { opacity: 0.4; cursor: default; }\n.result { font-size: 14px; }\n.result.ok { color: inherit; }\n.result.ok::before { content: \"\"; display: inline-block; width: 8px; height: 8px; margin-right: 8px; border-radius: 50%; background: var(--lamp); }\n.result.fail { color: var(--danger); }\n.link-btn { padding: 0; border: 0; background: none; color: var(--muted); font-size: 13px; text-decoration: underline; text-underline-offset: 0.2em; justify-self: start; }\n\n.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n\n@media (prefers-reduced-motion: reduce) {\n *, .panel { animation: none !important; transition: none !important; }\n}\n";
|
|
6
|
+
export declare const STYLES = "\n:host {\n --accent: var(--murmur-accent, #211c17);\n --accent-ink: var(--murmur-accent-ink, #f6efe0);\n --lamp: var(--murmur-lamp, #e0a032);\n --surface: #fbf8f1;\n --raised: #f2ece0;\n --ink: #211c17;\n --muted: #675c4f;\n --line: #e2d8c5;\n --focus: #b4541d;\n --danger: #a1301f;\n --radius: var(--murmur-radius, 16px);\n --shadow: 0 18px 48px -12px rgb(33 28 23 / 0.28), 0 4px 12px -4px rgb(33 28 23 / 0.16);\n --ease: cubic-bezier(0.16, 1, 0.3, 1);\n all: initial;\n position: fixed;\n z-index: var(--murmur-z, 2147483000);\n inset: auto 20px 20px auto;\n font-family: var(--murmur-font, inherit);\n font-size: 15px;\n line-height: 1.5;\n color: var(--ink);\n color-scheme: light;\n -webkit-font-smoothing: antialiased;\n}\n:host([position=\"bottom-left\"]) { inset: auto auto 20px 20px; }\n:host([inline]) { position: relative; inset: auto; z-index: auto; display: block; height: 100%; }\n:host([theme=\"dark\"]) { --surface: #1c1814; --raised: #26211b; --ink: #f2ead9; --muted: #b8ab95; --line: #3b342b; --focus: #f0a35c; --danger: #f08a73; color-scheme: dark; }\n@media (prefers-color-scheme: dark) {\n :host(:not([theme=\"light\"])) { --surface: #1c1814; --raised: #26211b; --ink: #f2ead9; --muted: #b8ab95; --line: #3b342b; --focus: #f0a35c; --danger: #f08a73; color-scheme: dark; }\n :host(:not([theme=\"light\"])) { --murmur-accent-default: #f2ead9; }\n}\n@media print { :host { display: none !important; } }\n* { box-sizing: border-box; }\n[hidden] { display: none !important; }\n::selection { background: color-mix(in oklab, var(--lamp) 45%, transparent); color: inherit; }\nbutton, input, select, textarea { font: inherit; color: inherit; }\nbutton { cursor: pointer; }\n:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }\na { color: inherit; text-underline-offset: 0.2em; text-decoration-thickness: 1px; }\n\n/* Launcher */\n.launcher {\n display: inline-flex; align-items: center; gap: 10px;\n height: 52px; padding: 0 20px 0 16px; border: 0; border-radius: 26px;\n background: var(--accent); color: var(--accent-ink);\n box-shadow: var(--shadow); font-weight: 600; letter-spacing: 0.01em;\n transition: transform 220ms var(--ease), box-shadow 220ms var(--ease);\n}\n.launcher:hover { transform: translateY(-2px); }\n.launcher:active { transform: translateY(0); }\n.launcher[aria-expanded=\"true\"] { display: none; }\n.lamp {\n width: 8px; height: 8px; border-radius: 50%; flex: none;\n background: var(--lamp); box-shadow: 0 0 0 3px color-mix(in oklab, var(--lamp) 28%, transparent);\n}\n.lamp.off { background: transparent; box-shadow: inset 0 0 0 1.5px var(--muted); }\n.lamp.busy { animation: blink 1.1s steps(2, jump-none) infinite; }\n@keyframes blink { 50% { opacity: 0.25; } }\n\n/* Panel */\n.panel {\n position: absolute; right: 0; bottom: 0;\n display: flex; flex-direction: column;\n width: min(400px, calc(100vw - 40px)); height: min(640px, calc(100dvh - 40px));\n background: var(--surface); border-radius: var(--radius); box-shadow: var(--shadow);\n overflow: hidden; transform-origin: bottom right;\n animation: rise 320ms var(--ease);\n}\n:host([position=\"bottom-left\"]) .panel { right: auto; left: 0; transform-origin: bottom left; }\n:host([inline]) .panel { position: static; width: 100%; height: 100%; box-shadow: none; animation: none; border-radius: inherit; }\n@keyframes rise { from { opacity: 0; transform: translateY(12px) scale(0.98); } }\n@media (max-width: 480px) {\n :host(:not([inline])) { inset: auto 12px 12px auto; }\n :host(:not([inline])[position=\"bottom-left\"]) { inset: auto auto 12px 12px; }\n :host(:not([inline])) .panel { position: fixed; inset: 0; width: 100%; height: 100dvh; border-radius: 0; }\n}\n\nheader {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 10px 14px 18px; background: var(--accent); color: var(--accent-ink);\n}\nheader h2 { flex: 1; margin: 0; font-size: 16px; font-weight: 650; letter-spacing: 0.005em; line-height: 1.25; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.icon-btn {\n display: inline-grid; place-items: center; width: 36px; height: 36px; flex: none;\n border: 0; border-radius: 10px; background: transparent; color: inherit;\n transition: background 160ms;\n}\n.icon-btn:hover { background: color-mix(in oklab, currentColor 14%, transparent); }\nheader .icon-btn:focus-visible { outline-color: var(--accent-ink); }\n\n/* Conversation */\n.log {\n flex: 1; overflow-y: auto; overscroll-behavior: contain;\n padding: 18px 18px 8px; display: flex; flex-direction: column; gap: 16px;\n scrollbar-width: thin; scrollbar-color: var(--line) transparent;\n}\n.msg { max-width: 100%; overflow-wrap: anywhere; }\n.msg p { margin: 0 0 0.6em; }\n.msg p:last-child { margin-bottom: 0; }\n.msg ul, .msg ol { margin: 0 0 0.6em; padding-left: 1.3em; }\n.msg li + li { margin-top: 0.2em; }\n.msg code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.88em; background: var(--raised); padding: 0.1em 0.35em; border-radius: 5px; }\n.msg pre { margin: 0 0 0.6em; padding: 10px 12px; background: var(--raised); border-radius: 10px; overflow-x: auto; }\n.msg pre code { background: none; padding: 0; }\n.msg .table { margin: 0 0 0.6em; overflow-x: auto; border-radius: 10px; box-shadow: inset 0 0 0 1px var(--line); }\n.msg table { width: 100%; border-collapse: collapse; font-size: 14px; }\n.msg th, .msg td { padding: 6px 10px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--line); }\n.msg tr:last-child td { border-bottom: 0; }\n.msg th { font-weight: 650; background: var(--raised); }\n.msg blockquote { margin: 0 0 0.6em; padding: 2px 0 2px 12px; color: var(--muted); box-shadow: inset 1px 0 0 var(--line); }\n.msg hr { margin: 0.8em 0; border: 0; border-top: 1px solid var(--line); }\n.user {\n align-self: flex-end; max-width: 85%;\n padding: 9px 14px; border-radius: 16px 16px 4px 16px;\n background: var(--accent); color: var(--accent-ink); white-space: pre-wrap;\n}\n.bot { align-self: stretch; }\n.greeting { color: var(--muted); }\n\n.cite {\n display: inline-grid; place-items: center; min-width: 1.45em; height: 1.45em; padding: 0 0.3em;\n margin: 0 0.1em; vertical-align: 0.12em;\n border-radius: 1em; box-shadow: inset 0 0 0 1.25px currentColor;\n font-size: 0.72em; font-weight: 700; line-height: 1; text-decoration: none;\n font-variant-numeric: tabular-nums; color: var(--muted);\n transition: background 160ms, color 160ms;\n}\n.cite:hover, .cite:focus-visible { background: var(--lamp); color: #211c17; box-shadow: none; }\n\n.sources { margin: 12px 0 0; padding: 0; list-style: none; display: grid; gap: 2px; }\n.sources-label { margin: 18px 0 2px; font-size: 12px; font-weight: 650; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }\n.sources a {\n display: grid; grid-template-columns: auto 1fr; align-items: baseline; gap: 10px;\n padding: 7px 10px; margin: 0 -10px; border-radius: 10px; text-decoration: none;\n}\n.sources a:hover { background: var(--raised); }\n.sources .cite { margin: 0; }\n.sources .where { min-width: 0; font-size: 14px; line-height: 1.35; }\n.sources .where span { color: var(--muted); }\n\n.status { display: flex; align-items: center; gap: 10px; color: var(--muted); font-size: 14px; }\n.error { color: var(--danger); }\n.error p { margin: 0 0 8px; }\n.text-btn {\n display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 9px;\n border: 0; background: var(--raised); color: var(--ink); font-size: 14px; font-weight: 600;\n}\n.text-btn:hover { background: var(--line); }\n\n/* Composer */\nform.composer {\n display: flex; align-items: flex-end; gap: 8px; margin: 8px 12px 0; padding: 6px 6px 6px 14px;\n border-radius: 14px; background: var(--raised);\n box-shadow: inset 0 0 0 1px var(--line); transition: box-shadow 160ms;\n}\nform.composer:focus-within { box-shadow: inset 0 0 0 1.5px var(--focus); }\ntextarea {\n flex: 1; min-height: 38px; max-height: 140px; padding: 8px 0; resize: none;\n border: 0; background: transparent; outline: none; line-height: 1.4;\n}\ntextarea:focus-visible { outline: none; }\ntextarea::placeholder, input::placeholder { color: var(--muted); opacity: 1; }\n.send {\n display: inline-grid; place-items: center; width: 38px; height: 38px; flex: none;\n border: 0; border-radius: 10px; background: var(--accent); color: var(--accent-ink);\n transition: opacity 160ms;\n}\n.send:disabled { opacity: 0.35; cursor: default; }\nfooter { padding: 8px 18px 12px; font-size: 12px; color: var(--muted); text-align: center; }\nfooter a { text-decoration: none; }\nfooter a:hover { text-decoration: underline; }\n\n/* Settings */\n.setup { flex: 1; overflow-y: auto; padding: 18px; display: grid; gap: 16px; align-content: start; }\n.setup p.intro { margin: 0; color: var(--muted); font-size: 14px; }\n.field { display: grid; gap: 6px; }\n.field > span { font-size: 13px; font-weight: 650; }\n.field small { color: var(--muted); font-size: 12.5px; }\n.field input:not([type=\"checkbox\"]), .field select {\n width: 100%; height: 40px; padding: 0 12px; border-radius: 10px;\n border: 0; background: var(--raised); box-shadow: inset 0 0 0 1px var(--line);\n}\n.field input:focus-visible, .field select:focus-visible { outline: none; box-shadow: inset 0 0 0 1.5px var(--focus); }\n.check { display: flex; align-items: center; gap: 10px; font-size: 14px; }\n.check input { width: 18px; height: 18px; accent-color: var(--accent); margin: 0; }\n.presets { display: flex; gap: 8px; flex-wrap: wrap; }\n.row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }\n.primary {\n display: inline-flex; align-items: center; justify-content: center; gap: 8px;\n height: 42px; padding: 0 18px; border: 0; border-radius: 11px;\n background: var(--accent); color: var(--accent-ink); font-weight: 650;\n}\n.primary:disabled { opacity: 0.4; cursor: default; }\n.result { font-size: 14px; }\n.result.ok { color: inherit; }\n.result.ok::before { content: \"\"; display: inline-block; width: 8px; height: 8px; margin-right: 8px; border-radius: 50%; background: var(--lamp); }\n.result.fail { color: var(--danger); }\n.local-choice { display: grid; gap: 6px; padding: 14px; border-radius: 12px; background: var(--raised); }\n.local-choice small { color: var(--muted); font-size: 12.5px; }\n.local-choice .link-btn { margin-top: 2px; }\np.or { margin: 0; color: var(--muted); font-size: 13px; text-align: center; }\n.consent { display: grid; gap: 10px; padding: 14px; border-radius: 12px; background: var(--raised); }\n.consent p { margin: 0; }\n.status .grow { display: grid; gap: 6px; flex: 1; }\nprogress { width: 100%; height: 6px; appearance: none; border: 0; border-radius: 3px; background: var(--line); overflow: hidden; }\nprogress::-webkit-progress-bar { background: var(--line); }\nprogress::-webkit-progress-value { background: var(--lamp); }\nprogress::-moz-progress-bar { background: var(--lamp); }\n.link-btn { padding: 0; border: 0; background: none; color: var(--muted); font-size: 13px; text-decoration: underline; text-underline-offset: 0.2em; justify-self: start; }\n\n.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n\n@media (prefers-reduced-motion: reduce) {\n *, .panel { animation: none !important; transition: none !important; }\n}\n";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "murmur-rag",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Embeddable AI chat for static sites: one script tag, a mini RAG over your own pages, any LLM through llm-connect (your proxy or the visitor's own key). No backend.",
|
|
5
5
|
"author": "maxgfr",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,6 +45,10 @@
|
|
|
45
45
|
"types": "./dist/types/llm/index.d.ts",
|
|
46
46
|
"default": "./dist/lib/llm/index.js"
|
|
47
47
|
},
|
|
48
|
+
"./local": {
|
|
49
|
+
"types": "./dist/types/local/index.d.ts",
|
|
50
|
+
"default": "./dist/lib/local/index.js"
|
|
51
|
+
},
|
|
48
52
|
"./widget": {
|
|
49
53
|
"types": "./dist/types/widget/index.d.ts",
|
|
50
54
|
"default": "./dist/lib/widget/index.js"
|
|
@@ -103,7 +107,8 @@
|
|
|
103
107
|
"linkedom": "^0.18.14",
|
|
104
108
|
"llm-connect": "^2.5.0",
|
|
105
109
|
"llm-models": "^1.14.0",
|
|
106
|
-
"minisearch": "^7.2.0"
|
|
110
|
+
"minisearch": "^7.2.0",
|
|
111
|
+
"@ai-sdk/provider": "^4.0.34"
|
|
107
112
|
},
|
|
108
113
|
"devDependencies": {
|
|
109
114
|
"@axe-core/playwright": "^4.14.0",
|
|
@@ -111,7 +116,17 @@
|
|
|
111
116
|
"@playwright/test": "^1.64.1",
|
|
112
117
|
"@semantic-release/exec": "^7.1.0",
|
|
113
118
|
"@types/bun": "^1.4.4",
|
|
119
|
+
"conventional-changelog-conventionalcommits": "^9",
|
|
114
120
|
"semantic-release": "^25.0.9",
|
|
115
|
-
"typescript": "^7.0.2"
|
|
121
|
+
"typescript": "^7.0.2",
|
|
122
|
+
"@huggingface/transformers": "^4.3.2"
|
|
123
|
+
},
|
|
124
|
+
"peerDependencies": {
|
|
125
|
+
"@huggingface/transformers": "^4.3.2"
|
|
126
|
+
},
|
|
127
|
+
"peerDependenciesMeta": {
|
|
128
|
+
"@huggingface/transformers": {
|
|
129
|
+
"optional": true
|
|
130
|
+
}
|
|
116
131
|
}
|
|
117
132
|
}
|