@ztimson/ai-utils 0.6.7 → 0.6.9
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/dist/asr.js +3 -3
- package/dist/asr.js.map +1 -1
- package/dist/asr.mjs +18 -18
- package/dist/asr.mjs.map +1 -1
- package/dist/embedder.js +1 -1
- package/dist/embedder.js.map +1 -1
- package/dist/embedder.mjs +5 -5
- package/dist/embedder.mjs.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +57 -58
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/asr.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=require("@xenova/transformers"),l=require("worker_threads"),k=require("node:fs"),P=require("wavefile"),g=require("node:child_process");function b(
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=require("@xenova/transformers"),l=require("worker_threads"),k=require("node:fs"),P=require("wavefile"),g=require("node:child_process");function b(t){const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const a in t)if(a!=="default"){const i=Object.getOwnPropertyDescriptor(t,a);Object.defineProperty(n,a,i.get?i:{enumerable:!0,get:()=>t[a]})}}return n.default=t,Object.freeze(n)}const S=b(k);let d;async function y(){return new Promise(t=>{const n=g.spawn("python3",["-c","import pyannote.audio"]);n.on("close",a=>t(a===0)),n.on("error",()=>t(!1))})}async function z(t,n){const a=`
|
|
2
2
|
import sys
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
@@ -17,6 +17,6 @@ for turn, _, speaker in diarization.itertracks(yield_label=True):
|
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
print(json.dumps(segments))
|
|
20
|
-
`;return new Promise((i,o)=>{let s="";const
|
|
21
|
-
`)}l.parentPort?.on("message",async({file:
|
|
20
|
+
`;return new Promise((i,o)=>{let s="";const r=g.spawn("python3",["-c",a,t]);r.stdout.on("data",e=>s+=e.toString()),r.stderr.on("data",e=>console.error(e.toString())),r.on("close",e=>{if(e===0)try{i(JSON.parse(s))}catch{o(new Error("Failed to parse diarization output"))}else o(new Error(`Python process exited with code ${e}`))}),r.on("error",o)})}function _(t,n){const a=new Map;let i=0;n.forEach(e=>{a.has(e.speaker)||a.set(e.speaker,++i)});const o=[];let s=-1,r="";return t.forEach(e=>{const u=e.timestamp[0],f=n.find(p=>u>=p.start&&u<=p.end),m=f?a.get(f.speaker):1;m!==s?(r&&o.push(`[speaker ${s}]: ${r.trim()}`),s=m,r=e.text):r+=e.text}),r&&o.push(`[speaker ${s}]: ${r.trim()}`),o.join(`
|
|
21
|
+
`)}l.parentPort?.on("message",async({file:t,speaker:n,model:a,modelDir:i})=>{try{console.log("worker",t),d||(d=await w.pipeline("automatic-speech-recognition",`Xenova/${a}`,{cache_dir:i,quantized:!0}));const o=new P.WaveFile(S.readFileSync(t));o.toBitDepth("32f"),o.toSampleRate(16e3);const s=o.getSamples();let r;if(Array.isArray(s)){const p=s[0],h=s[1];r=new Float32Array(p.length);for(let c=0;c<p.length;c++)r[c]=(p[c]+h[c])/2}else r=s;const e=await d(r,{return_timestamps:n?"word":!1});if(!n){l.parentPort?.postMessage({text:e.text?.trim()||null});return}if(!await y()){l.parentPort?.postMessage({text:e.text?.trim()||null,error:"Speaker diarization unavailable"});return}const f=await z(t,i),m=_(e.chunks||[],f);l.parentPort?.postMessage({text:m})}catch(o){l.parentPort?.postMessage({error:o.message})}});exports.canDiarization=y;
|
|
22
22
|
//# sourceMappingURL=asr.js.map
|
package/dist/asr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asr.js","sources":["../src/asr.ts"],"sourcesContent":["import { pipeline } from '@xenova/transformers';\nimport { parentPort } from 'worker_threads';\nimport * as fs from 'node:fs';\nimport wavefile from 'wavefile';\nimport { spawn } from 'node:child_process';\n\nlet whisperPipeline: any;\n\nexport async function canDiarization(): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst proc = spawn('python3', ['-c', 'import pyannote.audio']);\n\t\tproc.on('close', (code: number) => resolve(code === 0));\n\t\tproc.on('error', () => resolve(false));\n\t});\n}\n\nasync function runDiarization(audioPath: string, torchHome: string): Promise<any[]> {\n\tconst script = `\nimport sys\nimport json\nimport os\nfrom pyannote.audio import Pipeline\n\nos.environ['TORCH_HOME'] = \"${torchHome}\"\npipeline = Pipeline.from_pretrained(\"pyannote/speaker-diarization-3.1\")\ndiarization = pipeline(sys.argv[1])\n\nsegments = []\nfor turn, _, speaker in diarization.itertracks(yield_label=True):\n segments.append({\n \"start\": turn.start,\n \"end\": turn.end,\n \"speaker\": speaker\n })\n\nprint(json.dumps(segments))\n`;\n\n\treturn new Promise((resolve, reject) => {\n\t\tlet output = '';\n\t\tconst proc = spawn('python3', ['-c', script, audioPath]);\n\t\tproc.stdout.on('data', (data: Buffer) => output += data.toString());\n\t\tproc.stderr.on('data', (data: Buffer) => console.error(data.toString()));\n\t\tproc.on('close', (code: number) => {\n\t\t\tif(code === 0) {\n\t\t\t\ttry {\n\t\t\t\t\tresolve(JSON.parse(output));\n\t\t\t\t} catch (err) {\n\t\t\t\t\treject(new Error('Failed to parse diarization output'));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treject(new Error(`Python process exited with code ${code}`));\n\t\t\t}\n\t\t});\n\t\tproc.on('error', reject);\n\t});\n}\n\nfunction combineSpeakerTranscript(chunks: any[], speakers: any[]): string {\n\tconst speakerMap = new Map();\n\tlet speakerCount = 0;\n\tspeakers.forEach((seg: any) => {\n\t\tif(!speakerMap.has(seg.speaker)) speakerMap.set(seg.speaker, ++speakerCount);\n\t});\n\n\tconst lines: string[] = [];\n\tlet currentSpeaker = -1;\n\tlet currentText = '';\n\tchunks.forEach((chunk: any) => {\n\t\tconst time = chunk.timestamp[0];\n\t\tconst speaker = speakers.find((s: any) => time >= s.start && time <= s.end);\n\t\tconst speakerNum = speaker ? speakerMap.get(speaker.speaker) : 1;\n\t\tif (speakerNum !== currentSpeaker) {\n\t\t\tif(currentText) lines.push(`[speaker ${currentSpeaker}]: ${currentText.trim()}`);\n\t\t\tcurrentSpeaker = speakerNum;\n\t\t\tcurrentText = chunk.text;\n\t\t} else {\n\t\t\tcurrentText += chunk.text;\n\t\t}\n\t});\n\tif(currentText) lines.push(`[speaker ${currentSpeaker}]: ${currentText.trim()}`);\n\treturn lines.join('\\n');\n}\n\nparentPort?.on('message', async ({ file, speaker, model, modelDir }) => {\n\ttry {\n\t\tif(!whisperPipeline) whisperPipeline = await pipeline('automatic-speech-recognition', `Xenova/${model}`, {cache_dir: modelDir, quantized: true});\n\n\t\t// Prepare audio file (convert to mono channel wave)\n\t\tconst wav = new wavefile.WaveFile(fs.readFileSync(file));\n\t\twav.toBitDepth('32f');\n\t\twav.toSampleRate(16000);\n\t\tconst samples = wav.getSamples();\n\t\tlet buffer;\n\t\tif(Array.isArray(samples)) { // stereo to mono - average the channels\n\t\t\tconst left = samples[0];\n\t\t\tconst right = samples[1];\n\t\t\tbuffer = new Float32Array(left.length);\n\t\t\tfor (let i = 0; i < left.length; i++) buffer[i] = (left[i] + right[i]) / 2;\n\t\t} else {\n\t\t\tbuffer = samples;\n\t\t}\n\n\t\t// Transcribe\n\t\tconst transcriptResult = await whisperPipeline(buffer, {return_timestamps: speaker ? 'word' : false});\n\t\tif(!speaker) {\n\t\t\tparentPort?.postMessage({ text: transcriptResult.text?.trim() || null });\n\t\t\treturn;\n\t\t}\n\n\t\t// Speaker Diarization\n\t\tconst hasDiarization = await canDiarization();\n\t\tif(!hasDiarization) {\n\t\t\tparentPort?.postMessage({ text: transcriptResult.text?.trim() || null, error: 'Speaker diarization unavailable' });\n\t\t\treturn;\n\t\t}\n\n\t\tconst speakers = await runDiarization(file, modelDir);\n\t\tconst combined = combineSpeakerTranscript(transcriptResult.chunks || [], speakers);\n\t\tparentPort?.postMessage({ text: combined });\n\t} catch (err) {\n\t\tparentPort?.postMessage({ error: (err as Error).message });\n\t}\n});\n"],"names":["whisperPipeline","canDiarization","resolve","proc","spawn","code","runDiarization","audioPath","torchHome","script","reject","output","data","combineSpeakerTranscript","chunks","speakers","speakerMap","speakerCount","seg","lines","currentSpeaker","currentText","chunk","time","speaker","s","speakerNum","parentPort","file","model","modelDir","pipeline","wav","wavefile","fs","samples","buffer","left","right","i","transcriptResult","combined","err"],"mappings":"yfAMA,IAAIA,EAEJ,eAAsBC,GAAmC,CACxD,OAAO,IAAI,QAASC,GAAY,CAC/B,MAAMC,EAAOC,EAAAA,MAAM,UAAW,CAAC,KAAM,uBAAuB,CAAC,EAC7DD,EAAK,GAAG,QAAUE,GAAiBH,EAAQG,IAAS,CAAC,CAAC,EACtDF,EAAK,GAAG,QAAS,IAAMD,EAAQ,EAAK,CAAC,CACtC,CAAC,CACF,CAEA,eAAeI,EAAeC,EAAmBC,EAAmC,CACnF,MAAMC,EAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAMcD,CAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAetC,OAAO,IAAI,QAAQ,CAACN,EAASQ,IAAW,CACvC,IAAIC,EAAS,GACb,MAAMR,EAAOC,EAAAA,MAAM,UAAW,CAAC,KAAMK,EAAQF,CAAS,CAAC,EACvDJ,EAAK,OAAO,GAAG,OAASS,GAAiBD,GAAUC,EAAK,UAAU,EAClET,EAAK,OAAO,GAAG,OAASS,GAAiB,QAAQ,MAAMA,EAAK,SAAA,CAAU,CAAC,EACvET,EAAK,GAAG,QAAUE,GAAiB,CAClC,GAAGA,IAAS,EACX,GAAI,CACHH,EAAQ,KAAK,MAAMS,CAAM,CAAC,CAC3B,MAAc,CACbD,EAAO,IAAI,MAAM,oCAAoC,CAAC,CACvD,MAEAA,EAAO,IAAI,MAAM,mCAAmCL,CAAI,EAAE,CAAC,CAE7D,CAAC,EACDF,EAAK,GAAG,QAASO,CAAM,CACxB,CAAC,CACF,CAEA,SAASG,EAAyBC,EAAeC,EAAyB,CACzE,MAAMC,MAAiB,IACvB,IAAIC,EAAe,EACnBF,EAAS,QAASG,GAAa,CAC1BF,EAAW,IAAIE,EAAI,OAAO,GAAGF,EAAW,IAAIE,EAAI,QAAS,EAAED,CAAY,CAC5E,CAAC,EAED,MAAME,EAAkB,CAAA,EACxB,IAAIC,EAAiB,GACjBC,EAAc,GAClB,OAAAP,EAAO,QAASQ,GAAe,CAC9B,MAAMC,EAAOD,EAAM,UAAU,CAAC,EACxBE,EAAUT,EAAS,KAAMU,GAAWF,GAAQE,EAAE,OAASF,GAAQE,EAAE,GAAG,EACpEC,EAAaF,EAAUR,EAAW,IAAIQ,EAAQ,OAAO,EAAI,EAC3DE,IAAeN,GACfC,KAAmB,KAAK,YAAYD,CAAc,MAAMC,EAAY,KAAA,CAAM,EAAE,EAC/ED,EAAiBM,EACjBL,EAAcC,EAAM,MAEpBD,GAAeC,EAAM,IAEvB,CAAC,EACED,KAAmB,KAAK,YAAYD,CAAc,MAAMC,EAAY,KAAA,CAAM,EAAE,EACxEF,EAAM,KAAK;AAAA,CAAI,CACvB,CAEAQ,EAAAA,YAAY,GAAG,UAAW,MAAO,CAAE,KAAAC,EAAM,QAAAJ,EAAS,MAAAK,EAAO,SAAAC,KAAe,CACvE,GAAI,
|
|
1
|
+
{"version":3,"file":"asr.js","sources":["../src/asr.ts"],"sourcesContent":["import { pipeline } from '@xenova/transformers';\nimport { parentPort } from 'worker_threads';\nimport * as fs from 'node:fs';\nimport wavefile from 'wavefile';\nimport { spawn } from 'node:child_process';\n\nlet whisperPipeline: any;\n\nexport async function canDiarization(): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst proc = spawn('python3', ['-c', 'import pyannote.audio']);\n\t\tproc.on('close', (code: number) => resolve(code === 0));\n\t\tproc.on('error', () => resolve(false));\n\t});\n}\n\nasync function runDiarization(audioPath: string, torchHome: string): Promise<any[]> {\n\tconst script = `\nimport sys\nimport json\nimport os\nfrom pyannote.audio import Pipeline\n\nos.environ['TORCH_HOME'] = \"${torchHome}\"\npipeline = Pipeline.from_pretrained(\"pyannote/speaker-diarization-3.1\")\ndiarization = pipeline(sys.argv[1])\n\nsegments = []\nfor turn, _, speaker in diarization.itertracks(yield_label=True):\n segments.append({\n \"start\": turn.start,\n \"end\": turn.end,\n \"speaker\": speaker\n })\n\nprint(json.dumps(segments))\n`;\n\n\treturn new Promise((resolve, reject) => {\n\t\tlet output = '';\n\t\tconst proc = spawn('python3', ['-c', script, audioPath]);\n\t\tproc.stdout.on('data', (data: Buffer) => output += data.toString());\n\t\tproc.stderr.on('data', (data: Buffer) => console.error(data.toString()));\n\t\tproc.on('close', (code: number) => {\n\t\t\tif(code === 0) {\n\t\t\t\ttry {\n\t\t\t\t\tresolve(JSON.parse(output));\n\t\t\t\t} catch (err) {\n\t\t\t\t\treject(new Error('Failed to parse diarization output'));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treject(new Error(`Python process exited with code ${code}`));\n\t\t\t}\n\t\t});\n\t\tproc.on('error', reject);\n\t});\n}\n\nfunction combineSpeakerTranscript(chunks: any[], speakers: any[]): string {\n\tconst speakerMap = new Map();\n\tlet speakerCount = 0;\n\tspeakers.forEach((seg: any) => {\n\t\tif(!speakerMap.has(seg.speaker)) speakerMap.set(seg.speaker, ++speakerCount);\n\t});\n\n\tconst lines: string[] = [];\n\tlet currentSpeaker = -1;\n\tlet currentText = '';\n\tchunks.forEach((chunk: any) => {\n\t\tconst time = chunk.timestamp[0];\n\t\tconst speaker = speakers.find((s: any) => time >= s.start && time <= s.end);\n\t\tconst speakerNum = speaker ? speakerMap.get(speaker.speaker) : 1;\n\t\tif (speakerNum !== currentSpeaker) {\n\t\t\tif(currentText) lines.push(`[speaker ${currentSpeaker}]: ${currentText.trim()}`);\n\t\t\tcurrentSpeaker = speakerNum;\n\t\t\tcurrentText = chunk.text;\n\t\t} else {\n\t\t\tcurrentText += chunk.text;\n\t\t}\n\t});\n\tif(currentText) lines.push(`[speaker ${currentSpeaker}]: ${currentText.trim()}`);\n\treturn lines.join('\\n');\n}\n\nparentPort?.on('message', async ({ file, speaker, model, modelDir }) => {\n\ttry {\n\t\tconsole.log('worker', file);\n\t\tif(!whisperPipeline) whisperPipeline = await pipeline('automatic-speech-recognition', `Xenova/${model}`, {cache_dir: modelDir, quantized: true});\n\n\t\t// Prepare audio file (convert to mono channel wave)\n\t\tconst wav = new wavefile.WaveFile(fs.readFileSync(file));\n\t\twav.toBitDepth('32f');\n\t\twav.toSampleRate(16000);\n\t\tconst samples = wav.getSamples();\n\t\tlet buffer;\n\t\tif(Array.isArray(samples)) { // stereo to mono - average the channels\n\t\t\tconst left = samples[0];\n\t\t\tconst right = samples[1];\n\t\t\tbuffer = new Float32Array(left.length);\n\t\t\tfor (let i = 0; i < left.length; i++) buffer[i] = (left[i] + right[i]) / 2;\n\t\t} else {\n\t\t\tbuffer = samples;\n\t\t}\n\n\t\t// Transcribe\n\t\tconst transcriptResult = await whisperPipeline(buffer, {return_timestamps: speaker ? 'word' : false});\n\t\tif(!speaker) {\n\t\t\tparentPort?.postMessage({ text: transcriptResult.text?.trim() || null });\n\t\t\treturn;\n\t\t}\n\n\t\t// Speaker Diarization\n\t\tconst hasDiarization = await canDiarization();\n\t\tif(!hasDiarization) {\n\t\t\tparentPort?.postMessage({ text: transcriptResult.text?.trim() || null, error: 'Speaker diarization unavailable' });\n\t\t\treturn;\n\t\t}\n\n\t\tconst speakers = await runDiarization(file, modelDir);\n\t\tconst combined = combineSpeakerTranscript(transcriptResult.chunks || [], speakers);\n\t\tparentPort?.postMessage({ text: combined });\n\t} catch (err) {\n\t\tparentPort?.postMessage({ error: (err as Error).message });\n\t}\n});\n"],"names":["whisperPipeline","canDiarization","resolve","proc","spawn","code","runDiarization","audioPath","torchHome","script","reject","output","data","combineSpeakerTranscript","chunks","speakers","speakerMap","speakerCount","seg","lines","currentSpeaker","currentText","chunk","time","speaker","s","speakerNum","parentPort","file","model","modelDir","pipeline","wav","wavefile","fs","samples","buffer","left","right","i","transcriptResult","combined","err"],"mappings":"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"}
|
package/dist/asr.mjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { pipeline as
|
|
1
|
+
import { pipeline as w } from "@xenova/transformers";
|
|
2
2
|
import { parentPort as l } from "worker_threads";
|
|
3
|
-
import * as
|
|
3
|
+
import * as y from "node:fs";
|
|
4
4
|
import k from "wavefile";
|
|
5
5
|
import { spawn as h } from "node:child_process";
|
|
6
6
|
let d;
|
|
7
7
|
async function x() {
|
|
8
|
-
return new Promise((
|
|
8
|
+
return new Promise((o) => {
|
|
9
9
|
const a = h("python3", ["-c", "import pyannote.audio"]);
|
|
10
|
-
a.on("close", (
|
|
10
|
+
a.on("close", (i) => o(i === 0)), a.on("error", () => o(!1));
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
|
-
async function z(
|
|
14
|
-
const
|
|
13
|
+
async function z(o, a) {
|
|
14
|
+
const i = `
|
|
15
15
|
import sys
|
|
16
16
|
import json
|
|
17
17
|
import os
|
|
@@ -33,7 +33,7 @@ print(json.dumps(segments))
|
|
|
33
33
|
`;
|
|
34
34
|
return new Promise((s, r) => {
|
|
35
35
|
let n = "";
|
|
36
|
-
const t = h("python3", ["-c",
|
|
36
|
+
const t = h("python3", ["-c", i, o]);
|
|
37
37
|
t.stdout.on("data", (e) => n += e.toString()), t.stderr.on("data", (e) => console.error(e.toString())), t.on("close", (e) => {
|
|
38
38
|
if (e === 0)
|
|
39
39
|
try {
|
|
@@ -46,31 +46,31 @@ print(json.dumps(segments))
|
|
|
46
46
|
}), t.on("error", r);
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
|
-
function S(
|
|
50
|
-
const
|
|
49
|
+
function S(o, a) {
|
|
50
|
+
const i = /* @__PURE__ */ new Map();
|
|
51
51
|
let s = 0;
|
|
52
52
|
a.forEach((e) => {
|
|
53
|
-
|
|
53
|
+
i.has(e.speaker) || i.set(e.speaker, ++s);
|
|
54
54
|
});
|
|
55
55
|
const r = [];
|
|
56
56
|
let n = -1, t = "";
|
|
57
|
-
return
|
|
58
|
-
const m = e.timestamp[0], u = a.find((p) => m >= p.start && m <= p.end), f = u ?
|
|
57
|
+
return o.forEach((e) => {
|
|
58
|
+
const m = e.timestamp[0], u = a.find((p) => m >= p.start && m <= p.end), f = u ? i.get(u.speaker) : 1;
|
|
59
59
|
f !== n ? (t && r.push(`[speaker ${n}]: ${t.trim()}`), n = f, t = e.text) : t += e.text;
|
|
60
60
|
}), t && r.push(`[speaker ${n}]: ${t.trim()}`), r.join(`
|
|
61
61
|
`);
|
|
62
62
|
}
|
|
63
|
-
l?.on("message", async ({ file:
|
|
63
|
+
l?.on("message", async ({ file: o, speaker: a, model: i, modelDir: s }) => {
|
|
64
64
|
try {
|
|
65
|
-
d || (d = await
|
|
66
|
-
const r = new k.WaveFile(
|
|
65
|
+
console.log("worker", o), d || (d = await w("automatic-speech-recognition", `Xenova/${i}`, { cache_dir: s, quantized: !0 }));
|
|
66
|
+
const r = new k.WaveFile(y.readFileSync(o));
|
|
67
67
|
r.toBitDepth("32f"), r.toSampleRate(16e3);
|
|
68
68
|
const n = r.getSamples();
|
|
69
69
|
let t;
|
|
70
70
|
if (Array.isArray(n)) {
|
|
71
|
-
const p = n[0],
|
|
71
|
+
const p = n[0], g = n[1];
|
|
72
72
|
t = new Float32Array(p.length);
|
|
73
|
-
for (let c = 0; c < p.length; c++) t[c] = (p[c] +
|
|
73
|
+
for (let c = 0; c < p.length; c++) t[c] = (p[c] + g[c]) / 2;
|
|
74
74
|
} else
|
|
75
75
|
t = n;
|
|
76
76
|
const e = await d(t, { return_timestamps: a ? "word" : !1 });
|
|
@@ -82,7 +82,7 @@ l?.on("message", async ({ file: i, speaker: a, model: o, modelDir: s }) => {
|
|
|
82
82
|
l?.postMessage({ text: e.text?.trim() || null, error: "Speaker diarization unavailable" });
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
|
-
const u = await z(
|
|
85
|
+
const u = await z(o, s), f = S(e.chunks || [], u);
|
|
86
86
|
l?.postMessage({ text: f });
|
|
87
87
|
} catch (r) {
|
|
88
88
|
l?.postMessage({ error: r.message });
|
package/dist/asr.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asr.mjs","sources":["../src/asr.ts"],"sourcesContent":["import { pipeline } from '@xenova/transformers';\nimport { parentPort } from 'worker_threads';\nimport * as fs from 'node:fs';\nimport wavefile from 'wavefile';\nimport { spawn } from 'node:child_process';\n\nlet whisperPipeline: any;\n\nexport async function canDiarization(): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst proc = spawn('python3', ['-c', 'import pyannote.audio']);\n\t\tproc.on('close', (code: number) => resolve(code === 0));\n\t\tproc.on('error', () => resolve(false));\n\t});\n}\n\nasync function runDiarization(audioPath: string, torchHome: string): Promise<any[]> {\n\tconst script = `\nimport sys\nimport json\nimport os\nfrom pyannote.audio import Pipeline\n\nos.environ['TORCH_HOME'] = \"${torchHome}\"\npipeline = Pipeline.from_pretrained(\"pyannote/speaker-diarization-3.1\")\ndiarization = pipeline(sys.argv[1])\n\nsegments = []\nfor turn, _, speaker in diarization.itertracks(yield_label=True):\n segments.append({\n \"start\": turn.start,\n \"end\": turn.end,\n \"speaker\": speaker\n })\n\nprint(json.dumps(segments))\n`;\n\n\treturn new Promise((resolve, reject) => {\n\t\tlet output = '';\n\t\tconst proc = spawn('python3', ['-c', script, audioPath]);\n\t\tproc.stdout.on('data', (data: Buffer) => output += data.toString());\n\t\tproc.stderr.on('data', (data: Buffer) => console.error(data.toString()));\n\t\tproc.on('close', (code: number) => {\n\t\t\tif(code === 0) {\n\t\t\t\ttry {\n\t\t\t\t\tresolve(JSON.parse(output));\n\t\t\t\t} catch (err) {\n\t\t\t\t\treject(new Error('Failed to parse diarization output'));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treject(new Error(`Python process exited with code ${code}`));\n\t\t\t}\n\t\t});\n\t\tproc.on('error', reject);\n\t});\n}\n\nfunction combineSpeakerTranscript(chunks: any[], speakers: any[]): string {\n\tconst speakerMap = new Map();\n\tlet speakerCount = 0;\n\tspeakers.forEach((seg: any) => {\n\t\tif(!speakerMap.has(seg.speaker)) speakerMap.set(seg.speaker, ++speakerCount);\n\t});\n\n\tconst lines: string[] = [];\n\tlet currentSpeaker = -1;\n\tlet currentText = '';\n\tchunks.forEach((chunk: any) => {\n\t\tconst time = chunk.timestamp[0];\n\t\tconst speaker = speakers.find((s: any) => time >= s.start && time <= s.end);\n\t\tconst speakerNum = speaker ? speakerMap.get(speaker.speaker) : 1;\n\t\tif (speakerNum !== currentSpeaker) {\n\t\t\tif(currentText) lines.push(`[speaker ${currentSpeaker}]: ${currentText.trim()}`);\n\t\t\tcurrentSpeaker = speakerNum;\n\t\t\tcurrentText = chunk.text;\n\t\t} else {\n\t\t\tcurrentText += chunk.text;\n\t\t}\n\t});\n\tif(currentText) lines.push(`[speaker ${currentSpeaker}]: ${currentText.trim()}`);\n\treturn lines.join('\\n');\n}\n\nparentPort?.on('message', async ({ file, speaker, model, modelDir }) => {\n\ttry {\n\t\tif(!whisperPipeline) whisperPipeline = await pipeline('automatic-speech-recognition', `Xenova/${model}`, {cache_dir: modelDir, quantized: true});\n\n\t\t// Prepare audio file (convert to mono channel wave)\n\t\tconst wav = new wavefile.WaveFile(fs.readFileSync(file));\n\t\twav.toBitDepth('32f');\n\t\twav.toSampleRate(16000);\n\t\tconst samples = wav.getSamples();\n\t\tlet buffer;\n\t\tif(Array.isArray(samples)) { // stereo to mono - average the channels\n\t\t\tconst left = samples[0];\n\t\t\tconst right = samples[1];\n\t\t\tbuffer = new Float32Array(left.length);\n\t\t\tfor (let i = 0; i < left.length; i++) buffer[i] = (left[i] + right[i]) / 2;\n\t\t} else {\n\t\t\tbuffer = samples;\n\t\t}\n\n\t\t// Transcribe\n\t\tconst transcriptResult = await whisperPipeline(buffer, {return_timestamps: speaker ? 'word' : false});\n\t\tif(!speaker) {\n\t\t\tparentPort?.postMessage({ text: transcriptResult.text?.trim() || null });\n\t\t\treturn;\n\t\t}\n\n\t\t// Speaker Diarization\n\t\tconst hasDiarization = await canDiarization();\n\t\tif(!hasDiarization) {\n\t\t\tparentPort?.postMessage({ text: transcriptResult.text?.trim() || null, error: 'Speaker diarization unavailable' });\n\t\t\treturn;\n\t\t}\n\n\t\tconst speakers = await runDiarization(file, modelDir);\n\t\tconst combined = combineSpeakerTranscript(transcriptResult.chunks || [], speakers);\n\t\tparentPort?.postMessage({ text: combined });\n\t} catch (err) {\n\t\tparentPort?.postMessage({ error: (err as Error).message });\n\t}\n});\n"],"names":["whisperPipeline","canDiarization","resolve","proc","spawn","code","runDiarization","audioPath","torchHome","script","reject","output","data","combineSpeakerTranscript","chunks","speakers","speakerMap","speakerCount","seg","lines","currentSpeaker","currentText","chunk","time","speaker","s","speakerNum","parentPort","file","model","modelDir","pipeline","wav","wavefile","fs","samples","buffer","left","right","i","transcriptResult","combined","err"],"mappings":";;;;;AAMA,IAAIA;AAEJ,eAAsBC,IAAmC;AACxD,SAAO,IAAI,QAAQ,CAACC,MAAY;AAC/B,UAAMC,IAAOC,EAAM,WAAW,CAAC,MAAM,uBAAuB,CAAC;AAC7D,IAAAD,EAAK,GAAG,SAAS,CAACE,MAAiBH,EAAQG,MAAS,CAAC,CAAC,GACtDF,EAAK,GAAG,SAAS,MAAMD,EAAQ,EAAK,CAAC;AAAA,EACtC,CAAC;AACF;AAEA,eAAeI,EAAeC,GAAmBC,GAAmC;AACnF,QAAMC,IAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAMcD,CAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAetC,SAAO,IAAI,QAAQ,CAACN,GAASQ,MAAW;AACvC,QAAIC,IAAS;AACb,UAAMR,IAAOC,EAAM,WAAW,CAAC,MAAMK,GAAQF,CAAS,CAAC;AACvD,IAAAJ,EAAK,OAAO,GAAG,QAAQ,CAACS,MAAiBD,KAAUC,EAAK,UAAU,GAClET,EAAK,OAAO,GAAG,QAAQ,CAACS,MAAiB,QAAQ,MAAMA,EAAK,SAAA,CAAU,CAAC,GACvET,EAAK,GAAG,SAAS,CAACE,MAAiB;AAClC,UAAGA,MAAS;AACX,YAAI;AACH,UAAAH,EAAQ,KAAK,MAAMS,CAAM,CAAC;AAAA,QAC3B,QAAc;AACb,UAAAD,EAAO,IAAI,MAAM,oCAAoC,CAAC;AAAA,QACvD;AAAA;AAEA,QAAAA,EAAO,IAAI,MAAM,mCAAmCL,CAAI,EAAE,CAAC;AAAA,IAE7D,CAAC,GACDF,EAAK,GAAG,SAASO,CAAM;AAAA,EACxB,CAAC;AACF;AAEA,SAASG,EAAyBC,GAAeC,GAAyB;AACzE,QAAMC,wBAAiB,IAAA;AACvB,MAAIC,IAAe;AACnB,EAAAF,EAAS,QAAQ,CAACG,MAAa;AAC9B,IAAIF,EAAW,IAAIE,EAAI,OAAO,KAAGF,EAAW,IAAIE,EAAI,SAAS,EAAED,CAAY;AAAA,EAC5E,CAAC;AAED,QAAME,IAAkB,CAAA;AACxB,MAAIC,IAAiB,IACjBC,IAAc;AAClB,SAAAP,EAAO,QAAQ,CAACQ,MAAe;AAC9B,UAAMC,IAAOD,EAAM,UAAU,CAAC,GACxBE,IAAUT,EAAS,KAAK,CAACU,MAAWF,KAAQE,EAAE,SAASF,KAAQE,EAAE,GAAG,GACpEC,IAAaF,IAAUR,EAAW,IAAIQ,EAAQ,OAAO,IAAI;AAC/D,IAAIE,MAAeN,KACfC,OAAmB,KAAK,YAAYD,CAAc,MAAMC,EAAY,KAAA,CAAM,EAAE,GAC/ED,IAAiBM,GACjBL,IAAcC,EAAM,QAEpBD,KAAeC,EAAM;AAAA,EAEvB,CAAC,GACED,OAAmB,KAAK,YAAYD,CAAc,MAAMC,EAAY,KAAA,CAAM,EAAE,GACxEF,EAAM,KAAK;AAAA,CAAI;AACvB;AAEAQ,GAAY,GAAG,WAAW,OAAO,EAAE,MAAAC,GAAM,SAAAJ,GAAS,OAAAK,GAAO,UAAAC,QAAe;AACvE,MAAI;AACH,
|
|
1
|
+
{"version":3,"file":"asr.mjs","sources":["../src/asr.ts"],"sourcesContent":["import { pipeline } from '@xenova/transformers';\nimport { parentPort } from 'worker_threads';\nimport * as fs from 'node:fs';\nimport wavefile from 'wavefile';\nimport { spawn } from 'node:child_process';\n\nlet whisperPipeline: any;\n\nexport async function canDiarization(): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst proc = spawn('python3', ['-c', 'import pyannote.audio']);\n\t\tproc.on('close', (code: number) => resolve(code === 0));\n\t\tproc.on('error', () => resolve(false));\n\t});\n}\n\nasync function runDiarization(audioPath: string, torchHome: string): Promise<any[]> {\n\tconst script = `\nimport sys\nimport json\nimport os\nfrom pyannote.audio import Pipeline\n\nos.environ['TORCH_HOME'] = \"${torchHome}\"\npipeline = Pipeline.from_pretrained(\"pyannote/speaker-diarization-3.1\")\ndiarization = pipeline(sys.argv[1])\n\nsegments = []\nfor turn, _, speaker in diarization.itertracks(yield_label=True):\n segments.append({\n \"start\": turn.start,\n \"end\": turn.end,\n \"speaker\": speaker\n })\n\nprint(json.dumps(segments))\n`;\n\n\treturn new Promise((resolve, reject) => {\n\t\tlet output = '';\n\t\tconst proc = spawn('python3', ['-c', script, audioPath]);\n\t\tproc.stdout.on('data', (data: Buffer) => output += data.toString());\n\t\tproc.stderr.on('data', (data: Buffer) => console.error(data.toString()));\n\t\tproc.on('close', (code: number) => {\n\t\t\tif(code === 0) {\n\t\t\t\ttry {\n\t\t\t\t\tresolve(JSON.parse(output));\n\t\t\t\t} catch (err) {\n\t\t\t\t\treject(new Error('Failed to parse diarization output'));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treject(new Error(`Python process exited with code ${code}`));\n\t\t\t}\n\t\t});\n\t\tproc.on('error', reject);\n\t});\n}\n\nfunction combineSpeakerTranscript(chunks: any[], speakers: any[]): string {\n\tconst speakerMap = new Map();\n\tlet speakerCount = 0;\n\tspeakers.forEach((seg: any) => {\n\t\tif(!speakerMap.has(seg.speaker)) speakerMap.set(seg.speaker, ++speakerCount);\n\t});\n\n\tconst lines: string[] = [];\n\tlet currentSpeaker = -1;\n\tlet currentText = '';\n\tchunks.forEach((chunk: any) => {\n\t\tconst time = chunk.timestamp[0];\n\t\tconst speaker = speakers.find((s: any) => time >= s.start && time <= s.end);\n\t\tconst speakerNum = speaker ? speakerMap.get(speaker.speaker) : 1;\n\t\tif (speakerNum !== currentSpeaker) {\n\t\t\tif(currentText) lines.push(`[speaker ${currentSpeaker}]: ${currentText.trim()}`);\n\t\t\tcurrentSpeaker = speakerNum;\n\t\t\tcurrentText = chunk.text;\n\t\t} else {\n\t\t\tcurrentText += chunk.text;\n\t\t}\n\t});\n\tif(currentText) lines.push(`[speaker ${currentSpeaker}]: ${currentText.trim()}`);\n\treturn lines.join('\\n');\n}\n\nparentPort?.on('message', async ({ file, speaker, model, modelDir }) => {\n\ttry {\n\t\tconsole.log('worker', file);\n\t\tif(!whisperPipeline) whisperPipeline = await pipeline('automatic-speech-recognition', `Xenova/${model}`, {cache_dir: modelDir, quantized: true});\n\n\t\t// Prepare audio file (convert to mono channel wave)\n\t\tconst wav = new wavefile.WaveFile(fs.readFileSync(file));\n\t\twav.toBitDepth('32f');\n\t\twav.toSampleRate(16000);\n\t\tconst samples = wav.getSamples();\n\t\tlet buffer;\n\t\tif(Array.isArray(samples)) { // stereo to mono - average the channels\n\t\t\tconst left = samples[0];\n\t\t\tconst right = samples[1];\n\t\t\tbuffer = new Float32Array(left.length);\n\t\t\tfor (let i = 0; i < left.length; i++) buffer[i] = (left[i] + right[i]) / 2;\n\t\t} else {\n\t\t\tbuffer = samples;\n\t\t}\n\n\t\t// Transcribe\n\t\tconst transcriptResult = await whisperPipeline(buffer, {return_timestamps: speaker ? 'word' : false});\n\t\tif(!speaker) {\n\t\t\tparentPort?.postMessage({ text: transcriptResult.text?.trim() || null });\n\t\t\treturn;\n\t\t}\n\n\t\t// Speaker Diarization\n\t\tconst hasDiarization = await canDiarization();\n\t\tif(!hasDiarization) {\n\t\t\tparentPort?.postMessage({ text: transcriptResult.text?.trim() || null, error: 'Speaker diarization unavailable' });\n\t\t\treturn;\n\t\t}\n\n\t\tconst speakers = await runDiarization(file, modelDir);\n\t\tconst combined = combineSpeakerTranscript(transcriptResult.chunks || [], speakers);\n\t\tparentPort?.postMessage({ text: combined });\n\t} catch (err) {\n\t\tparentPort?.postMessage({ error: (err as Error).message });\n\t}\n});\n"],"names":["whisperPipeline","canDiarization","resolve","proc","spawn","code","runDiarization","audioPath","torchHome","script","reject","output","data","combineSpeakerTranscript","chunks","speakers","speakerMap","speakerCount","seg","lines","currentSpeaker","currentText","chunk","time","speaker","s","speakerNum","parentPort","file","model","modelDir","pipeline","wav","wavefile","fs","samples","buffer","left","right","i","transcriptResult","combined","err"],"mappings":"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"}
|
package/dist/embedder.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";const
|
|
1
|
+
"use strict";const i=require("@xenova/transformers"),r=require("worker_threads");let e;r.parentPort?.on("message",async({text:t,model:a,modelDir:n})=>{e||(e=await i.pipeline("feature-extraction","Xenova/"+a,{quantized:!0,cache_dir:n}));const o=await e(t,{pooling:"mean",normalize:!0}),s=Array.from(o.data);r.parentPort?.postMessage({embedding:s})});
|
|
2
2
|
//# sourceMappingURL=embedder.js.map
|
package/dist/embedder.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedder.js","sources":["../src/embedder.ts"],"sourcesContent":["import { pipeline } from '@xenova/transformers';\nimport { parentPort } from 'worker_threads';\n\nlet embedder: any;\n\nparentPort?.on('message', async ({
|
|
1
|
+
{"version":3,"file":"embedder.js","sources":["../src/embedder.ts"],"sourcesContent":["import { pipeline } from '@xenova/transformers';\nimport { parentPort } from 'worker_threads';\n\nlet embedder: any;\n\nparentPort?.on('message', async ({text, model, modelDir }) => {\n\tif(!embedder) embedder = await pipeline('feature-extraction', 'Xenova/' + model, {quantized: true, cache_dir: modelDir});\n\tconst output = await embedder(text, { pooling: 'mean', normalize: true });\n\tconst embedding = Array.from(output.data);\n\tparentPort?.postMessage({embedding});\n});\n"],"names":["embedder","parentPort","text","model","modelDir","pipeline","output","embedding"],"mappings":"iFAGA,IAAIA,EAEJC,EAAAA,YAAY,GAAG,UAAW,MAAO,CAAC,KAAAC,EAAM,MAAAC,EAAO,SAAAC,KAAe,CACzDJ,IAAUA,EAAW,MAAMK,EAAAA,SAAS,qBAAsB,UAAYF,EAAO,CAAC,UAAW,GAAM,UAAWC,EAAS,GACvH,MAAME,EAAS,MAAMN,EAASE,EAAM,CAAE,QAAS,OAAQ,UAAW,GAAM,EAClEK,EAAY,MAAM,KAAKD,EAAO,IAAI,EACxCL,cAAY,YAAY,CAAC,UAAAM,EAAU,CACpC,CAAC"}
|
package/dist/embedder.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { pipeline as
|
|
1
|
+
import { pipeline as m } from "@xenova/transformers";
|
|
2
2
|
import { parentPort as t } from "worker_threads";
|
|
3
3
|
let e;
|
|
4
|
-
t?.on("message", async ({
|
|
5
|
-
e || (e = await
|
|
6
|
-
const
|
|
7
|
-
t?.postMessage({
|
|
4
|
+
t?.on("message", async ({ text: a, model: o, modelDir: r }) => {
|
|
5
|
+
e || (e = await m("feature-extraction", "Xenova/" + o, { quantized: !0, cache_dir: r }));
|
|
6
|
+
const i = await e(a, { pooling: "mean", normalize: !0 }), n = Array.from(i.data);
|
|
7
|
+
t?.postMessage({ embedding: n });
|
|
8
8
|
});
|
|
9
9
|
//# sourceMappingURL=embedder.mjs.map
|
package/dist/embedder.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedder.mjs","sources":["../src/embedder.ts"],"sourcesContent":["import { pipeline } from '@xenova/transformers';\nimport { parentPort } from 'worker_threads';\n\nlet embedder: any;\n\nparentPort?.on('message', async ({
|
|
1
|
+
{"version":3,"file":"embedder.mjs","sources":["../src/embedder.ts"],"sourcesContent":["import { pipeline } from '@xenova/transformers';\nimport { parentPort } from 'worker_threads';\n\nlet embedder: any;\n\nparentPort?.on('message', async ({text, model, modelDir }) => {\n\tif(!embedder) embedder = await pipeline('feature-extraction', 'Xenova/' + model, {quantized: true, cache_dir: modelDir});\n\tconst output = await embedder(text, { pooling: 'mean', normalize: true });\n\tconst embedding = Array.from(output.data);\n\tparentPort?.postMessage({embedding});\n});\n"],"names":["embedder","parentPort","text","model","modelDir","pipeline","output","embedding"],"mappings":";;AAGA,IAAIA;AAEJC,GAAY,GAAG,WAAW,OAAO,EAAC,MAAAC,GAAM,OAAAC,GAAO,UAAAC,QAAe;AAC7D,EAAIJ,MAAUA,IAAW,MAAMK,EAAS,sBAAsB,YAAYF,GAAO,EAAC,WAAW,IAAM,WAAWC,GAAS;AACvH,QAAME,IAAS,MAAMN,EAASE,GAAM,EAAE,SAAS,QAAQ,WAAW,IAAM,GAClEK,IAAY,MAAM,KAAKD,EAAO,IAAI;AACxC,EAAAL,GAAY,YAAY,EAAC,WAAAM,GAAU;AACpC,CAAC;"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const E=require("node:os"),f=require("@ztimson/utils"),v=require("@anthropic-ai/sdk"),$=require("openai"),x=require("worker_threads"),S=require("url"),w=require("path"),j=require("./asr.js"),L=require("tesseract.js");require("./embedder.js");const N=require("cheerio"),T=require("@ztimson/node-utils");var g=typeof document<"u"?document.currentScript:null;function q(u){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(u){for(const e in u)if(e!=="default"){const t=Object.getOwnPropertyDescriptor(u,e);Object.defineProperty(r,e,t.get?t:{enumerable:!0,get:()=>u[e]})}}return r.default=u,Object.freeze(r)}const C=q(E),D=q(N);class k{}class P extends k{constructor(r,e,t){super(),this.ai=r,this.apiToken=e,this.model=t,this.client=new v.Anthropic({apiKey:e})}client;toStandard(r){const e=Date.now(),t=[];for(let o of r)if(typeof o.content=="string")t.push({timestamp:e,...o});else{const s=o.content?.filter(n=>n.type=="text").map(n=>n.text).join(`
|
|
2
2
|
|
|
3
3
|
`);s&&t.push({timestamp:e,role:o.role,content:s}),o.content.forEach(n=>{if(n.type=="tool_use")t.push({timestamp:e,role:"tool",id:n.id,name:n.name,args:n.input,content:void 0});else if(n.type=="tool_result"){const i=t.findLast(m=>m.id==n.tool_use_id);i&&(i[n.is_error?"error":"content"]=n.content)}})}return t}fromStandard(r){for(let e=0;e<r.length;e++)if(r[e].role=="tool"){const t=r[e];r.splice(e,1,{role:"assistant",content:[{type:"tool_use",id:t.id,name:t.name,input:t.args}]},{role:"user",content:[{type:"tool_result",tool_use_id:t.id,is_error:!!t.error,content:t.error||t.content}]}),e++}return r.map(({timestamp:e,...t})=>t)}ask(r,e={}){const t=new AbortController;return Object.assign(new Promise(async o=>{let s=this.fromStandard([...e.history||[],{role:"user",content:r,timestamp:Date.now()}]);const n=e.tools||this.ai.options.llm?.tools||[],i={model:e.model||this.model,max_tokens:e.max_tokens||this.ai.options.llm?.max_tokens||4096,system:e.system||this.ai.options.llm?.system||"",temperature:e.temperature||this.ai.options.llm?.temperature||.7,tools:n.map(d=>({name:d.name,description:d.description,input_schema:{type:"object",properties:d.args?f.objectMap(d.args,(c,l)=>({...l,required:void 0})):{},required:d.args?Object.entries(d.args).filter(c=>c[1].required).map(c=>c[0]):[]},fn:void 0})),messages:s,stream:!!e.stream};let m,a=!0;do{if(m=await this.client.messages.create(i).catch(c=>{throw c.message+=`
|
|
4
4
|
|
|
@@ -7,12 +7,12 @@ ${JSON.stringify(s,null,2)}`,c}),e.stream){a?a=!1:e.stream({text:`
|
|
|
7
7
|
|
|
8
8
|
`}),m.content=[];for await(const c of m){if(t.signal.aborted)break;if(c.type==="content_block_start")c.content_block.type==="text"?m.content.push({type:"text",text:""}):c.content_block.type==="tool_use"&&m.content.push({type:"tool_use",id:c.content_block.id,name:c.content_block.name,input:""});else if(c.type==="content_block_delta")if(c.delta.type==="text_delta"){const l=c.delta.text;m.content.at(-1).text+=l,e.stream({text:l})}else c.delta.type==="input_json_delta"&&(m.content.at(-1).input+=c.delta.partial_json);else if(c.type==="content_block_stop"){const l=m.content.at(-1);l.input!=null&&(l.input=l.input?f.JSONAttemptParse(l.input,{}):{})}else if(c.type==="message_stop")break}}const d=m.content.filter(c=>c.type==="tool_use");if(d.length&&!t.signal.aborted){s.push({role:"assistant",content:m.content});const c=await Promise.all(d.map(async l=>{const p=n.find(f.findByProp("name",l.name));if(e.stream&&e.stream({tool:l.name}),!p)return{tool_use_id:l.id,is_error:!0,content:"Tool not found"};try{const h=await p.fn(l.input,e?.stream,this.ai);return{type:"tool_result",tool_use_id:l.id,content:f.JSONSanitize(h)}}catch(h){return{type:"tool_result",tool_use_id:l.id,is_error:!0,content:h?.message||h?.toString()||"Unknown"}}}));s.push({role:"user",content:c}),i.messages=s}}while(!t.signal.aborted&&m.content.some(d=>d.type==="tool_use"));s.push({role:"assistant",content:m.content.filter(d=>d.type=="text").map(d=>d.text).join(`
|
|
9
9
|
|
|
10
|
-
`)}),s=this.toStandard(s),e.stream&&e.stream({done:!0}),e.history&&e.history.splice(0,e.history.length,...s),o(s.at(-1)?.content)}),{abort:()=>t.abort()})}}class
|
|
10
|
+
`)}),s=this.toStandard(s),e.stream&&e.stream({done:!0}),e.history&&e.history.splice(0,e.history.length,...s),o(s.at(-1)?.content)}),{abort:()=>t.abort()})}}class _ extends k{constructor(r,e,t,o){super(),this.ai=r,this.host=e,this.token=t,this.model=o,this.client=new $.OpenAI(f.clean({baseURL:e,apiKey:t}))}client;toStandard(r){for(let e=0;e<r.length;e++){const t=r[e];if(t.role==="assistant"&&t.tool_calls){const o=t.tool_calls.map(s=>({role:"tool",id:s.id,name:s.function.name,args:f.JSONAttemptParse(s.function.arguments,{}),timestamp:t.timestamp}));r.splice(e,1,...o),e+=o.length-1}else if(t.role==="tool"&&t.content){const o=r.find(s=>t.tool_call_id==s.id);o&&(t.content.includes('"error":')?o.error=t.content:o.content=t.content),r.splice(e,1),e--}r[e]?.timestamp||(r[e].timestamp=Date.now())}return r}fromStandard(r){return r.reduce((e,t)=>{if(t.role==="tool")e.push({role:"assistant",content:null,tool_calls:[{id:t.id,type:"function",function:{name:t.name,arguments:JSON.stringify(t.args)}}],refusal:null,annotations:[]},{role:"tool",tool_call_id:t.id,content:t.error||t.content});else{const{timestamp:o,...s}=t;e.push(s)}return e},[])}ask(r,e={}){const t=new AbortController;return Object.assign(new Promise(async(o,s)=>{e.system&&e.history?.[0]?.role!="system"&&e.history?.splice(0,0,{role:"system",content:e.system,timestamp:Date.now()});let n=this.fromStandard([...e.history||[],{role:"user",content:r,timestamp:Date.now()}]);const i=e.tools||this.ai.options.llm?.tools||[],m={model:e.model||this.model,messages:n,stream:!!e.stream,max_tokens:e.max_tokens||this.ai.options.llm?.max_tokens||4096,temperature:e.temperature||this.ai.options.llm?.temperature||.7,tools:i.map(c=>({type:"function",function:{name:c.name,description:c.description,parameters:{type:"object",properties:c.args?f.objectMap(c.args,(l,p)=>({...p,required:void 0})):{},required:c.args?Object.entries(c.args).filter(l=>l[1].required).map(l=>l[0]):[]}}}))};let a,d=!0;do{if(a=await this.client.chat.completions.create(m).catch(l=>{throw l.message+=`
|
|
11
11
|
|
|
12
12
|
Messages:
|
|
13
13
|
${JSON.stringify(n,null,2)}`,l}),e.stream){d?d=!1:e.stream({text:`
|
|
14
14
|
|
|
15
|
-
`}),a.choices=[{message:{content:"",tool_calls:[]}}];for await(const l of a){if(t.signal.aborted)break;l.choices[0].delta.content&&(a.choices[0].message.content+=l.choices[0].delta.content,e.stream({text:l.choices[0].delta.content})),l.choices[0].delta.tool_calls&&(a.choices[0].message.tool_calls=l.choices[0].delta.tool_calls)}}const c=a.choices[0].message.tool_calls||[];if(c.length&&!t.signal.aborted){n.push(a.choices[0].message);const l=await Promise.all(c.map(async p=>{const h=i?.find(f.findByProp("name",p.function.name));if(e.stream&&e.stream({tool:p.function.name}),!h)return{role:"tool",tool_call_id:p.id,content:'{"error": "Tool not found"}'};try{const y=f.JSONAttemptParse(p.function.arguments,{}),
|
|
15
|
+
`}),a.choices=[{message:{content:"",tool_calls:[]}}];for await(const l of a){if(t.signal.aborted)break;l.choices[0].delta.content&&(a.choices[0].message.content+=l.choices[0].delta.content,e.stream({text:l.choices[0].delta.content})),l.choices[0].delta.tool_calls&&(a.choices[0].message.tool_calls=l.choices[0].delta.tool_calls)}}const c=a.choices[0].message.tool_calls||[];if(c.length&&!t.signal.aborted){n.push(a.choices[0].message);const l=await Promise.all(c.map(async p=>{const h=i?.find(f.findByProp("name",p.function.name));if(e.stream&&e.stream({tool:p.function.name}),!h)return{role:"tool",tool_call_id:p.id,content:'{"error": "Tool not found"}'};try{const y=f.JSONAttemptParse(p.function.arguments,{}),b=await h.fn(y,e.stream,this.ai);return{role:"tool",tool_call_id:p.id,content:f.JSONSanitize(b)}}catch(y){return{role:"tool",tool_call_id:p.id,content:f.JSONSanitize({error:y?.message||y?.toString()||"Unknown"})}}}));n.push(...l),m.messages=n}}while(!t.signal.aborted&&a.choices?.[0]?.message?.tool_calls?.length);n.push({role:"assistant",content:a.choices[0].message.content||""}),n=this.toStandard(n),e.stream&&e.stream({done:!0}),e.history&&e.history.splice(0,e.history.length,...n),o(n.at(-1)?.content)}),{abort:()=>t.abort()})}}class J{constructor(r){this.ai=r,r.options.llm?.models&&Object.entries(r.options.llm.models).forEach(([e,t])=>{this.defaultModel||(this.defaultModel=e),t.proto=="anthropic"?this.models[e]=new P(this.ai,t.token,e):t.proto=="ollama"?this.models[e]=new _(this.ai,t.host,"not-needed",e):t.proto=="openai"&&(this.models[e]=new _(this.ai,t.host||null,t.token,e))})}models={};defaultModel;ask(r,e={}){const t=e.model||this.defaultModel;if(!this.models[t])throw new Error(`Model does not exist: ${t}`);let o=()=>{};return Object.assign(new Promise(async s=>{if(e.history||(e.history=[]),e.memory){e.system=(e.system||"")+`
|
|
16
16
|
You have passive persistent memory never make any mention of your memory capabilities and what you can/cannot remember
|
|
17
17
|
`;const i=async(a,d,c=50)=>{const[l,p]=await Promise.all([d?this.embedding(d):Promise.resolve(null),a?this.embedding(a):Promise.resolve(null)]);return(e.memory||[]).map(h=>({...h,score:l?this.cosineSimilarity(h.embeddings[0],l[0].embedding):1})).filter(h=>h.score>=.8).map(h=>({...h,score:p?this.cosineSimilarity(h.embeddings[1],p[0].embedding):h.score})).filter(h=>h.score>=.2).toSorted((h,y)=>h.score-y.score).slice(0,c)},m=await i(r);m.length&&e.history.push({role:"assistant",content:`Things I remembered:
|
|
18
18
|
`+m.map(a=>`${a.owner}: ${a.fact}`).join(`
|
|
@@ -20,9 +20,9 @@ You have passive persistent memory never make any mention of your memory capabil
|
|
|
20
20
|
|
|
21
21
|
${a.map(h=>`${h.role}: ${h.content}`).join(`
|
|
22
22
|
|
|
23
|
-
`)}`,{model:o?.model,temperature:o?.temperature||.3}),c=new Date,l=await Promise.all((d?.facts||[])?.map(async([h,y])=>{const
|
|
23
|
+
`)}`,{model:o?.model,temperature:o?.temperature||.3}),c=new Date,l=await Promise.all((d?.facts||[])?.map(async([h,y])=>{const b=await Promise.all([this.embedding(h),this.embedding(`${h}: ${y}`)]);return{owner:h,fact:y,embeddings:[b[0][0].embedding,b[1][0].embedding],timestamp:c}})),p=[{role:"assistant",content:`Conversation Summary: ${d?.summary}`,timestamp:Date.now()},...m];return i&&p.splice(0,0,i),{history:p,memory:l}}cosineSimilarity(r,e){if(r.length!==e.length)throw new Error("Vectors must be same length");let t=0,o=0,s=0;for(let i=0;i<r.length;i++)t+=r[i]*e[i],o+=r[i]*r[i],s+=e[i]*e[i];const n=Math.sqrt(o)*Math.sqrt(s);return n===0?0:t/n}chunk(r,e=500,t=50){const o=(m,a="")=>m?Object.entries(m).flatMap(([d,c])=>{const l=a?`${a}${isNaN(+d)?`.${d}`:`[${d}]`}`:d;return typeof c=="object"&&!Array.isArray(c)?o(c,l):`${l}: ${Array.isArray(c)?c.join(", "):c}`}):[],n=(typeof r=="object"?o(r):r.split(`
|
|
24
24
|
`)).flatMap(m=>[...m.split(/\s+/).filter(Boolean),`
|
|
25
25
|
`]),i=[];for(let m=0;m<n.length;){let a="",d=m;for(;d<n.length;){const l=a+(a?" ":"")+n[d];if(this.estimateTokens(l.replace(/\s*\n\s*/g,`
|
|
26
26
|
`))>e&&a)break;a=l,d++}const c=a.replace(/\s*\n\s*/g,`
|
|
27
|
-
`).trim();c&&i.push(c),m=Math.max(d-t,d===m?m+1:d)}return i}embedding(r,e=500,t=50){const o=n=>new Promise((i,m)=>{const a=new x.Worker(
|
|
27
|
+
`).trim();c&&i.push(c),m=Math.max(d-t,d===m?m+1:d)}return i}embedding(r,e=500,t=50){const o=n=>new Promise((i,m)=>{const a=new x.Worker(w.join(w.dirname(S.fileURLToPath(typeof document>"u"?require("url").pathToFileURL(__filename).href:g&&g.tagName.toUpperCase()==="SCRIPT"&&g.src||new URL("index.js",document.baseURI).href)),"embedder.js")),d=({embedding:l})=>{a.terminate(),i(l)},c=l=>{a.terminate(),m(l)};a.on("message",d),a.on("error",c),a.on("exit",l=>{l!==0&&m(new Error(`Worker exited with code ${l}`))}),a.postMessage({text:n,model:this.ai.options?.embedder||"bge-small-en-v1.5",modelDir:this.ai.options.path})}),s=this.chunk(r,e,t);return Promise.all(s.map(async(n,i)=>({index:i,embedding:await o(n),text:n,tokens:this.estimateTokens(n)})))}estimateTokens(r){const e=JSON.stringify(r);return Math.ceil(e.length/4*1.2)}fuzzyMatch(r,...e){if(e.length<2)throw new Error("Requires at least 2 strings to compare");const t=(n,i=10)=>n.toLowerCase().split("").map((m,a)=>m.charCodeAt(0)*(a+1)%i/i).slice(0,i),o=t(r),s=e.map(n=>t(n)).map(n=>this.cosineSimilarity(o,n));return{avg:s.reduce((n,i)=>n+i,0)/s.length,max:Math.max(...s),similarities:s}}async json(r,e){let t=await this.ask(r,{system:"Respond using a JSON blob matching any provided examples",...e});if(!t)return{};const o=/```(?:.+)?\s*([\s\S]*?)```/.exec(t),s=o?o[1].trim():t;return f.JSONAttemptParse(s,{})}summarize(r,e,t){return this.ask(r,{system:`Generate a brief summary <= ${e} tokens. Output nothing else`,temperature:.3,...t})}}class O{constructor(r){this.ai=r}asr(r,e={}){const{model:t=this.ai.options.asr||"whisper-base",speaker:o=!1}=e;let s=!1;const n=()=>{s=!0},i=new Promise((m,a)=>{const d=new x.Worker(w.join(w.dirname(S.fileURLToPath(typeof document>"u"?require("url").pathToFileURL(__filename).href:g&&g.tagName.toUpperCase()==="SCRIPT"&&g.src||new URL("index.js",document.baseURI).href)),"asr.js")),c=({text:p,warning:h,error:y})=>{d.terminate(),!s&&(y?a(new Error(y)):(h&&console.warn(h),m(p)))},l=p=>{d.terminate(),s||a(p)};d.on("message",c),d.on("error",l),d.on("exit",p=>{p!==0&&!s&&a(new Error(`Worker exited with code ${p}`))}),d.postMessage({file:r,model:t,speaker:o,modelDir:this.ai.options.path})});return Object.assign(i,{abort:n})}canDiarization=j.canDiarization}class M{constructor(r){this.ai=r}ocr(r){let e;const t=new Promise(async o=>{e=await L.createWorker(this.ai.options.ocr||"eng",2,{cachePath:this.ai.options.path});const{data:s}=await e.recognize(r);await e.terminate(),o(s.text.trim()||null)});return Object.assign(t,{abort:()=>e?.terminate()})}}class W{constructor(r){this.options=r,r.path||(r.path=C.tmpdir()),process.env.TRANSFORMERS_CACHE=r.path,this.audio=new O(this),this.language=new J(this),this.vision=new M(this)}audio;language;vision}const U={name:"cli",description:"Use the command line interface, returns any output",args:{command:{type:"string",description:"Command to run",required:!0}},fn:u=>T.$`${u.command}`},z={name:"get_datetime",description:"Get current UTC date / time",args:{},fn:async()=>new Date().toUTCString()},I={name:"exec",description:"Run code/scripts",args:{language:{type:"string",description:"Execution language",enum:["cli","node","python"],required:!0},code:{type:"string",description:"Code to execute",required:!0}},fn:async(u,r,e)=>{try{switch(u.type){case"bash":return await U.fn({command:u.code},r,e);case"node":return await A.fn({code:u.code},r,e);case"python":return await R.fn({code:u.code},r,e)}}catch(t){return{error:t?.message||t.toString()}}}},F={name:"fetch",description:"Make HTTP request to URL",args:{url:{type:"string",description:"URL to fetch",required:!0},method:{type:"string",description:"HTTP method to use",enum:["GET","POST","PUT","DELETE"],default:"GET"},headers:{type:"object",description:"HTTP headers to send",default:{}},body:{type:"object",description:"HTTP body to send"}},fn:u=>new f.Http({url:u.url,headers:u.headers}).request({method:u.method||"GET",body:u.body})},A={name:"exec_javascript",description:"Execute commonjs javascript",args:{code:{type:"string",description:"CommonJS javascript",required:!0}},fn:async u=>{const r=f.consoleInterceptor(null),e=await f.fn({console:r},u.code,!0).catch(t=>r.output.error.push(t));return{...r.output,return:e,stdout:void 0,stderr:void 0}}},R={name:"exec_javascript",description:"Execute commonjs javascript",args:{code:{type:"string",description:"CommonJS javascript",required:!0}},fn:async u=>({result:T.$Sync`python -c "${u.code}"`})},H={name:"read_webpage",description:"Extract clean, structured content from a webpage. Use after web_search to read specific URLs",args:{url:{type:"string",description:"URL to extract content from",required:!0},focus:{type:"string",description:'Optional: What aspect to focus on (e.g., "pricing", "features", "contact info")'}},fn:async u=>{const r=await fetch(u.url,{headers:{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}}).then(n=>n.text()).catch(n=>{throw new Error(`Failed to fetch: ${n.message}`)}),e=D.load(r);e('script, style, nav, footer, header, aside, iframe, noscript, [role="navigation"], [role="banner"], .ad, .ads, .cookie, .popup').remove();const t={title:e('meta[property="og:title"]').attr("content")||e("title").text()||"",description:e('meta[name="description"]').attr("content")||e('meta[property="og:description"]').attr("content")||""};let o="";const s=["article","main",'[role="main"]',".content",".post",".entry","body"];for(const n of s){const i=e(n).first();if(i.length&&i.text().trim().length>200){o=i.text();break}}return o||(o=e("body").text()),o=o.replace(/\s+/g," ").trim().slice(0,8e3),{url:u.url,title:t.title.trim(),description:t.description.trim(),content:o,focus:u.focus}}},B={name:"web_search",description:"Use duckduckgo (anonymous) to find find relevant online resources. Returns a list of URLs that works great with the `read_webpage` tool",args:{query:{type:"string",description:"Search string",required:!0},length:{type:"string",description:"Number of results to return",default:5}},fn:async u=>{const r=await fetch(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(u.query)}`,{headers:{"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64)","Accept-Language":"en-US,en;q=0.9"}}).then(s=>s.text());let e,t=/<a .*?href="(.+?)".+?<\/a>/g;const o=new f.ASet;for(;(e=t.exec(r))!==null;){let s=/uddg=(.+)&?/.exec(decodeURIComponent(e[1]))?.[1];if(s&&(s=decodeURIComponent(s)),s&&o.add(s),o.size>=(u.length||5))break}return o}};exports.canDiarization=j.canDiarization;exports.Ai=W;exports.Anthropic=P;exports.Audio=O;exports.CliTool=U;exports.DateTimeTool=z;exports.ExecTool=I;exports.FetchTool=F;exports.JSTool=A;exports.LLMProvider=k;exports.OpenAi=_;exports.PythonTool=R;exports.ReadWebpageTool=H;exports.Vision=M;exports.WebSearchTool=B;
|
|
28
28
|
//# sourceMappingURL=index.js.map
|