@gjsify/embeddings 0.0.0-stage → 0.64.26
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 +88 -2
- package/lib/esm/_virtual/_rolldown/runtime.js +1 -0
- package/lib/esm/embedder.js +1 -0
- package/lib/esm/engine.js +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/llama-bins.js +1 -0
- package/lib/esm/llama-engine.js +1 -0
- package/lib/esm/llama-models.js +1 -0
- package/lib/esm/llama-prompts.js +1 -0
- package/lib/esm/llama-runtime.js +1 -0
- package/lib/esm/llama.js +1 -0
- package/lib/esm/pool.js +1 -0
- package/lib/esm/vectors.js +1 -0
- package/lib/types/embedder.d.ts +22 -0
- package/lib/types/embedder.spec.d.ts +2 -0
- package/lib/types/engine.d.ts +43 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/llama-bins.d.ts +22 -0
- package/lib/types/llama-engine.d.ts +81 -0
- package/lib/types/llama-models.d.ts +27 -0
- package/lib/types/llama-prompts.d.ts +18 -0
- package/lib/types/llama-runtime.d.ts +28 -0
- package/lib/types/llama.d.ts +7 -0
- package/lib/types/llama.spec.d.ts +2 -0
- package/lib/types/pool.d.ts +15 -0
- package/lib/types/vectors.d.ts +12 -0
- package/lib/types/vectors.spec.d.ts +2 -0
- package/package.json +113 -5
package/README.md
CHANGED
|
@@ -1,3 +1,89 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @gjsify/embeddings
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Text embeddings in the WebML CG lifecycle shape: an `Embedder` with `availability()`, `create({ monitor, signal })` and `destroy()`, vectors checked before they reach the caller, and a local llama.cpp engine that runs GGUF models on Node and on GJS. The engine is a parameter, so an app can plug in a remote provider without gjsify shipping it.
|
|
4
|
+
|
|
5
|
+
Part of the [gjsify](https://github.com/gjsify/gjsify) project. **Tier 3 (experimental, ADR 0003)**: the `Embedder` and the engine's availability logic are unit-tested on Node and GJS; the llama.cpp engine has been run by hand against a real model, on CPU and on Vulkan. Design: ADR 0132.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
gjsify install @gjsify/embeddings
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The root export needs nothing else. The llama.cpp engine needs its optional peers:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
gjsify install node-llama-cpp @node-llama-cpp/linux-x64 @node-llama-cpp/linux-x64-vulkan
|
|
17
|
+
gjsify install @gjsify/napi # GJS only
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { Embedder } from '@gjsify/embeddings';
|
|
24
|
+
import { llamaEmbeddings } from '@gjsify/embeddings/llama';
|
|
25
|
+
|
|
26
|
+
const engine = llamaEmbeddings({ dimensions: 256 });
|
|
27
|
+
|
|
28
|
+
if ((await Embedder.availability({ engine })) !== 'unavailable') {
|
|
29
|
+
const embedder = await Embedder.create({
|
|
30
|
+
engine,
|
|
31
|
+
monitor: (m) => m.addEventListener('downloadprogress', (e) => console.log(e.loaded)),
|
|
32
|
+
});
|
|
33
|
+
const query = await embedder.embedQuery('how do I renew a passport');
|
|
34
|
+
const docs = await embedder.embedDocuments([{ title: 'Passports', text: 'Renewal takes six weeks.' }]);
|
|
35
|
+
await embedder.destroy();
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`create()` downloads the model when it is missing (through `@gjsify/model-store`, digest-checked) and loads it. `availability()` reads no model bytes: it checks for the prebuilt binaries, the model file, and whether the machine has the memory for it (`@gjsify/machine-fit`).
|
|
40
|
+
|
|
41
|
+
## Exports
|
|
42
|
+
|
|
43
|
+
- **`.`** — `Embedder` and the types around it.
|
|
44
|
+
- **`./engine`** — the `EmbeddingEngine` / `EmbeddingEngineFactory` interfaces, plus `checkVector`, `normalize`, `truncateVector` and `embedAcross` for writing an engine of your own, local or remote.
|
|
45
|
+
- **`./llama`** — `llamaEmbeddings(options)`, the model manifest `EMBEDDING_MODELS` and `collectGarbage`.
|
|
46
|
+
|
|
47
|
+
What `Embedder` guarantees for every engine:
|
|
48
|
+
|
|
49
|
+
- Every vector is a finite `Float32Array` of `dimensions` length, one per input.
|
|
50
|
+
- One call runs at a time; later calls queue behind it.
|
|
51
|
+
- An aborted `signal` rejects at once. A signal that is already aborted never reaches the engine.
|
|
52
|
+
- `destroy()` waits for the calls it has already accepted, then frees the model. Later calls reject.
|
|
53
|
+
|
|
54
|
+
## The llama.cpp engine
|
|
55
|
+
|
|
56
|
+
| Option | Default | |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `model` | `embeddinggemma-2-q8_0` | an id from `EMBEDDING_MODELS` |
|
|
59
|
+
| `modelPath` | — | a file you already have, checked against the manifest |
|
|
60
|
+
| `dimensions` | the model's native size | one of the model's `truncations` (EmbeddingGemma: 768 or 256) |
|
|
61
|
+
| `gpu` | `auto` | a `GpuSetting`, e.g. `parseGpuSetting(env.MYAPP_GPU)` from `@gjsify/machine-fit` |
|
|
62
|
+
| `gpuGuardDir` | shared by all gjsify GPU engines | where the GPU guard keeps its pin |
|
|
63
|
+
| `collectGarbage` | `true` | run the GC after each `embedDocuments` on GJS |
|
|
64
|
+
|
|
65
|
+
Input longer than the model's context is cut, not rejected.
|
|
66
|
+
|
|
67
|
+
**The GPU guard.** A Vulkan driver can hang a process instead of failing. Before going to the GPU, the engine leaves a pin in `gpuGuardDir` and removes it when the work finishes. A pin left over from a crash keeps the next run on the CPU and reports it through `onGpuPinned`. `gpu: 'on'` removes the pin.
|
|
68
|
+
|
|
69
|
+
**`collectGarbage` on GJS.** SpiderMonkey decides when to collect from the size of the JS heap, but an embedding run's garbage is mostly native memory behind small wrapper objects. One measured run of 1500 documents grew from 0.85 GB to 1.8 GB; with one collection per batch it stayed at 0.85 GB. A collection inside a database transaction has broken commits before, so if you embed inside one, set `collectGarbage: false` and call `collectGarbage()` after the commit.
|
|
70
|
+
|
|
71
|
+
### Bundling for GJS
|
|
72
|
+
|
|
73
|
+
`./llama` imports `node-llama-cpp` statically. Evaluating the module loads no addon and no model. The import is static because a bundled dynamic `import('node-llama-cpp')` never settles on GJS: its modules with top-level await become lazy async initialisers, and the import hangs without an error.
|
|
74
|
+
|
|
75
|
+
The app bundle needs one alias, `ipull` → `ipull/node`:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{ "gjsify": { "aliases": { "ipull": "ipull/node" } } }
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
gjsify resolves with the `browser` condition, and ipull's browser build lacks the `downloadFile` that node-llama-cpp's model downloader imports. This engine never downloads through ipull, but the barrel still has to link.
|
|
82
|
+
|
|
83
|
+
On GJS the native addon is loaded through `@gjsify/napi`. Start the app with `gjsify run` so the napi typelib is found.
|
|
84
|
+
|
|
85
|
+
On macOS and Windows the llama.cpp engine runs on Node only, because the GJS route loads node-llama-cpp's Linux prebuilt binaries.
|
|
86
|
+
|
|
87
|
+
## License
|
|
88
|
+
|
|
89
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,__name=(t,n)=>e(t,`name`,{value:n,configurable:!0});export{__name};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{checkVector as e}from"./vectors.js";function untilAborted(e){if(e===void 0)return{promise:new Promise(()=>{}),dispose(){}};let onAbort=()=>{};return{promise:new Promise((t,n)=>{onAbort=()=>n(e.reason),e.addEventListener(`abort`,onAbort,{once:!0})}),dispose:()=>e.removeEventListener(`abort`,onAbort)}}var t=class Embedder{#e;#t=Promise.resolve();#n=!1;constructor(e){this.#e=e}static availability(e){return e.engine.availability()}static async create(e){e.signal?.throwIfAborted();let t=await e.engine.create({monitor:e.monitor,signal:e.signal});if(e.signal?.aborted&&(await t.destroy().catch(()=>void 0),e.signal.throwIfAborted()),!Number.isInteger(t.dimensions)||t.dimensions<=0)throw await t.destroy().catch(()=>void 0),Error(`engine ${t.info.name} reports ${t.dimensions} dimensions`);return new Embedder(t)}get engine(){return this.#e.info}get modelId(){return this.#e.modelId}get dimensions(){return this.#e.dimensions}get backend(){return this.#e.backend}async embedQuery(e,t={}){let[n]=await this.#r(1,t,t=>this.#e.embedQuery(e,{signal:t}).then(e=>[e]));return n}async embedDocuments(e,t={}){return e.length===0?[]:this.#r(e.length,t,t=>this.#e.embedDocuments(e,{signal:t}))}async destroy(){this.#n||(this.#n=!0,await this.#t.catch(()=>void 0),await this.#e.destroy())}#r(t,n,r){if(this.#n)return Promise.reject(Error(`Embedder was destroyed`));let{signal:i}=n;if(i?.aborted)return Promise.reject(i.reason);let a=this.#t.catch(()=>void 0).then(async()=>{i?.throwIfAborted();let n=await r(i);if(n.length!==t)throw Error(`engine ${this.#e.info.name} returned ${n.length} vectors for ${t} inputs`);for(let t of n){if(!(t instanceof Float32Array))throw TypeError(`engine returned a vector that is not a Float32Array`);if(t.length!==this.#e.dimensions)throw Error(`engine returned ${t.length} dimensions, ${this.#e.dimensions} expected`);e(t)}return n});this.#t=a;let o=untilAborted(i);return Promise.race([a,o.promise]).finally(o.dispose)}};export{t as Embedder};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{checkVector as e,normalize as t,truncateVector as n}from"./vectors.js";import{embedAcross as r}from"./pool.js";export{e as checkVector,r as embedAcross,t as normalize,n as truncateVector};
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Embedder as e}from"./embedder.js";export{e as Embedder};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{runningOnGjs as e}from"./llama-runtime.js";import{join as t}from"node:path";import n from"node:process";import{hasVulkan as r}from"@gjsify/machine-fit";const i=`_nlcBuildMetadata.json`;async function loadLinuxX64(){return import(`@node-llama-cpp/linux-x64`)}async function loadLinuxArm64(){return import(`@node-llama-cpp/linux-arm64`)}async function loadLinuxX64Vulkan(){return import(`@node-llama-cpp/linux-x64-vulkan`)}const a={"linux-x64":loadLinuxX64,"linux-x64-vulkan":loadLinuxX64Vulkan,"linux-arm64":loadLinuxArm64},o=new Set([`darwin-arm64`,`darwin-x64`,`win32-arm64`,`win32-x64`,`linux-arm64`,`linux-x64`]);function currentTarget(e=n){return`${e.platform}-${e.arch}`}function hasPrebuiltBins(t=currentTarget(),n=e()){return n?a[t]!==void 0:o.has(t)}function backendCandidates(e,t,n){let i=`${e}-vulkan`;return a[i]===void 0||t===`off`?[e]:t===`on`||r(n)?[i,e]:[e]}async function prebuiltBinsDir(e){let n=a[e];if(n===void 0)throw Error(`no prebuilt llama.cpp binaries for ${e}`);let{binsDir:r}=(await n()).getBinsDir();return t(r,e)}export{i as BUILD_METADATA_FILE,backendCandidates,currentTarget,hasPrebuiltBins,prebuiltBinsDir};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{normalize as e,truncateVector as t}from"./vectors.js";import{embedAcross as n}from"./pool.js";import{collectGarbage as r,runningOnGjs as i}from"./llama-runtime.js";import{BUILD_METADATA_FILE as a,backendCandidates as o,currentTarget as s,hasPrebuiltBins as c,prebuiltBinsDir as l}from"./llama-bins.js";import{EMBEDDING_MODELS as u}from"./llama-models.js";import{documentPrompt as d,fitTokens as f,queryPrompt as p}from"./llama-prompts.js";import{join as m}from"node:path";import{checkFit as h,createGpuGuard as g,defaultGuardDir as _,readMachineMemory as v,systemProbe as y}from"@gjsify/machine-fit";import{readFileSync as b}from"node:fs";import{ModelStore as x}from"@gjsify/model-store";const S={name:`llama.cpp`,locality:`local`},C=2,w=2;async function loadNapi(){return import(`@gjsify/napi`)}async function createLlamaOnGjs(e,t,n){let[{loadAddon:r},i]=await Promise.all([loadNapi(),l(t)]),o=m(i,`llama-addon.node`);return e.Llama._create({bindings:r(o),bindingPath:o,extBackendsPath:i,buildType:`prebuilt`,buildMetadata:JSON.parse(b(m(i,a),`utf8`)),logLevel:e.LlamaLogLevel.warn,logger:e.Llama.defaultConsoleLogger,maxThreads:n,vramPadding:0,ramPadding:0,debug:!1,numa:!1,tempDir:!1})}async function createLlamaOnNode(e,t){if(!t)return{llama:await e.getLlama({gpu:`auto`}),onCpu:!1};try{return{llama:await e.getLlama({gpu:!1,build:`never`}),onCpu:!1}}catch{let t=await e.getLlama({gpu:`auto`,build:`never`});return{llama:t,onCpu:t.gpu!==!1}}}async function openOn(e,t,n=!1){let r=e.gpu!==!1&&!n,i=r?Math.max(1,Math.trunc(t.gpuContexts)):1,a=await e.loadModel({modelPath:t.modelPath,...n?{gpuLayers:0}:{}}),o=[];try{for(let e=0;e<i;e++)o.push(await a.createEmbeddingContext({contextSize:t.entry.contextTokens,threads:t.threads}));return{llama:e,model:a,contexts:o,gpu:r}}catch(e){throw await Promise.all(o.map(e=>e.dispose().catch(()=>void 0))),await a.dispose(),e}}async function openOnGjs(e,t,n){for(let[r,i]of t.entries()){let a=r===t.length-1,o;try{if(o=await createLlamaOnGjs(e,i,n.threads),!a&&o.gpu===!1)throw Error(`${i} found no GPU`);return await openOn(o,n)}catch(e){if(await o?.dispose().catch(()=>void 0),a)throw e}}throw Error(`no llama.cpp build to try`)}let T=!1;function defaultOnGpuPinned(e){console.error(`The GPU is switched off for embeddings: process ${e.pid} died while using it (${e.at}). Turn the GPU setting on to try it again.`)}function storeFor(e){return e.store??new x({capability:`embeddings`,models:u,defaultModel:`embeddinggemma-2-q8_0`,appDataDirs:e.appDataDirs})}function createLlamaEmbeddings(a,l={}){let u=storeFor(l),m=u.require(l.model),b=l.dimensions??m.dimensions;if(!m.truncations.includes(b))throw RangeError(`${m.id} gives ${m.truncations.join(` or `)} dimensions, not ${b}`);let x=l.gpu??`auto`,C=l.threads??2,w,gpuGuard=()=>w??=g({dir:l.gpuGuardDir??_(`gjsify`)}),gpuBarred=()=>{if(x===`off`)return!0;if(x===`on`)return gpuGuard().clear(),!1;let e=gpuGuard().pin();return e===void 0?!1:(T||(T=!0,(l.onGpuPinned??defaultOnGpuPinned)(e)),!0)},openSession=async(e,t)=>{let n=t||gpuBarred(),open=async()=>{let t={modelPath:e,entry:m,threads:C,gpuContexts:l.gpuContexts??2};if(i()){let e=s();return openOnGjs(a,n?[e]:o(e,x,l.probe??y()),t)}let{llama:r,onCpu:c}=await createLlamaOnNode(a,n);return openOn(r,t,c)};return n?open():gpuGuard().during(open)},closeSession=async e=>{let close=async()=>{await Promise.all(e.contexts.map(e=>e.dispose())),await e.model.dispose(),await e.llama.dispose()};await(e.gpu?gpuGuard().during(close):close())};return{info:S,async availability(){let e=l.probe??y();if(!c(s(e)))return`unavailable`;let t=v(e);return h(m.footprintBytes,t).placement===`none`?`unavailable`:u.availability(m.id,{path:l.modelPath})},async create(i={}){let a=await u.install(m.id,{path:l.modelPath,monitor:i.monitor,signal:i.signal});i.signal?.throwIfAborted();let o=await openSession(a,!1),embedOnce=async(n,r,i)=>{let{vector:a}=await r.getEmbeddingFor(f(n.model,i,m.contextTokens));return b===m.dimensions?e(a):t(a,b)},embed=async e=>{let t=o,{vectors:r,failure:i}=await n(t.contexts,e,(e,n)=>embedOnce(t,e,n));if(i!==void 0){if(!t.gpu)throw i.error;await closeSession(t),o=await openSession(a,!0);for(let[t,n]of e.entries())r[t]??=await embedOnce(o,o.contexts[0],n)}return r},guarded=e=>o.gpu?gpuGuard().during(e):e();return{info:S,modelId:m.modelId,dimensions:b,get backend(){return o.gpu?`gpu`:`cpu`},async embedQuery(e){let[t]=await guarded(()=>embed([p(m,e)]));return t},async embedDocuments(e){let t=await guarded(()=>embed(e.map(e=>d(m,e))));return l.collectGarbage!==!1&&r(),t},async destroy(){await closeSession(o)}}}}}export{w as DEFAULT_GPU_CONTEXTS,C as DEFAULT_THREADS,S as LLAMA_ENGINE,createLlamaEmbeddings};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`embeddinggemma-2-q8_0`,t=[{id:e,file:`embeddinggemma-2-Q8_0.gguf`,url:`https://huggingface.co/ggml-org/embeddinggemma-2-GGUF/resolve/58faf90940886df2d62be555c8765d00dac362c5/embeddinggemma-2-Q8_0.gguf`,bytes:309855456,sha256:`2188ac1deca4b77dffefd603c2776a9d76d9d74ec01841392982ebb840b09135`,license:`Apache-2.0`,redistributable:!0,modelId:`ggml-org/embeddinggemma-2-GGUF:Q8_0`,description:`EmbeddingGemma 2, Q8_0 (296 MiB): multilingual, 768 dimensions, truncatable to 256.`,dimensions:768,truncations:[768,256],queryTemplate:`task: search result | query: {text}`,documentTemplate:`title: {title} | text: {text}`,emptyTitle:`none`,contextTokens:512,footprintBytes:850*1024*1024}];export{e as DEFAULT_EMBEDDING_MODEL,t as EMBEDDING_MODELS};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function queryPrompt(e,t){return e.queryTemplate.replace(`{text}`,()=>t)}function documentPrompt(e,t){let n=t.title?.trim()||e.emptyTitle;return e.documentTemplate.replace(`{title}`,()=>n).replace(`{text}`,()=>t.text)}function fitTokens(e,t,n){let r=n-8,i=e.tokenize(t);return i.length<=r?t:e.detokenize(i.slice(0,r))}export{documentPrompt,fitTokens,queryPrompt};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function runningOnGjs(e=globalThis){return e.imports!==void 0&&e.process?.versions?.gjs!==void 0}function collectGarbage(){(globalThis.imports?.system)?.gc?.()}export{collectGarbage,runningOnGjs};
|
package/lib/esm/llama.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{collectGarbage as e}from"./llama-runtime.js";import{DEFAULT_EMBEDDING_MODEL as t,EMBEDDING_MODELS as n}from"./llama-models.js";import{DEFAULT_GPU_CONTEXTS as r,DEFAULT_THREADS as i,LLAMA_ENGINE as a,createLlamaEmbeddings as o}from"./llama-engine.js";import*as s from"node-llama-cpp";function llamaEmbeddings(e={}){return o(s,e)}export{t as DEFAULT_EMBEDDING_MODEL,r as DEFAULT_GPU_CONTEXTS,i as DEFAULT_THREADS,n as EMBEDDING_MODELS,a as LLAMA_ENGINE,e as collectGarbage,llamaEmbeddings};
|
package/lib/esm/pool.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";async function embedAcross(e,t,n){let r=Array.from({length:t.length}),i,a=0;return await Promise.all(e.map(async e=>{for(;i===void 0&&a<t.length;){let o=a++;try{r[o]=await n(e,t[o])}catch(e){i??={error:e}}}})),i===void 0?{vectors:r}:{vectors:r,failure:i}}export{embedAcross};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function checkVector(e){if(e.length===0)throw Error(`embedding engine returned an empty vector`);let t=0;for(let n=0;n<e.length;n++){let r=e[n];if(!Number.isFinite(r))throw Error(`embedding engine returned a non-finite value`);t+=r*r}if(t===0)throw Error(`embedding engine returned a zero-norm vector`)}function normalize(e){checkVector(e);let t=0;for(let n=0;n<e.length;n++)t+=e[n]*e[n];let n=Math.sqrt(t),r=new Float32Array(e.length);for(let t=0;t<e.length;t++)r[t]=e[t]/n;return r}function truncateVector(e,t){if(e.length<t)throw Error(`cannot truncate a ${e.length}-dimensional vector to ${t}`);return normalize(Array.prototype.slice.call(e,0,t))}export{checkVector,normalize,truncateVector};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Availability, EmbedCallOptions, EmbedDocument, EmbeddingEngineFactory, EngineCreateOptions, EngineInfo } from './engine.js';
|
|
2
|
+
export interface EmbedderAvailabilityOptions {
|
|
3
|
+
readonly engine: EmbeddingEngineFactory;
|
|
4
|
+
}
|
|
5
|
+
export interface EmbedderCreateOptions extends EngineCreateOptions {
|
|
6
|
+
readonly engine: EmbeddingEngineFactory;
|
|
7
|
+
}
|
|
8
|
+
export declare class Embedder {
|
|
9
|
+
#private;
|
|
10
|
+
private constructor();
|
|
11
|
+
static availability(options: EmbedderAvailabilityOptions): Promise<Availability>;
|
|
12
|
+
/** Downloads the model when the engine needs one and it is missing, then loads it. */
|
|
13
|
+
static create(options: EmbedderCreateOptions): Promise<Embedder>;
|
|
14
|
+
get engine(): EngineInfo;
|
|
15
|
+
get modelId(): string;
|
|
16
|
+
get dimensions(): number;
|
|
17
|
+
get backend(): 'gpu' | 'cpu' | undefined;
|
|
18
|
+
embedQuery(text: string, options?: EmbedCallOptions): Promise<Float32Array>;
|
|
19
|
+
embedDocuments(documents: readonly EmbedDocument[], options?: EmbedCallOptions): Promise<Float32Array[]>;
|
|
20
|
+
/** Waits for a running call, then frees the model. Later calls reject. */
|
|
21
|
+
destroy(): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { CreateMonitorCallback } from '@gjsify/model-store';
|
|
2
|
+
export { checkVector, normalize, truncateVector } from './vectors.js';
|
|
3
|
+
export { embedAcross, type PoolResult } from './pool.js';
|
|
4
|
+
/** The WebML CG availability states. */
|
|
5
|
+
export type Availability = 'unavailable' | 'downloadable' | 'downloading' | 'available';
|
|
6
|
+
/**
|
|
7
|
+
* A document as an embedding model sees it. Title and text stay apart because
|
|
8
|
+
* instruction-tuned models give the title its own prompt field.
|
|
9
|
+
*/
|
|
10
|
+
export interface EmbedDocument {
|
|
11
|
+
readonly title?: string;
|
|
12
|
+
readonly text: string;
|
|
13
|
+
}
|
|
14
|
+
/** Which engine runs, so a surface can say where the text goes (ADR 0132 § 3). */
|
|
15
|
+
export interface EngineInfo {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly locality: 'local' | 'remote';
|
|
18
|
+
}
|
|
19
|
+
export interface EmbedCallOptions {
|
|
20
|
+
readonly signal?: AbortSignal;
|
|
21
|
+
}
|
|
22
|
+
/** A loaded model. Every method may be called again after the previous call settled. */
|
|
23
|
+
export interface EmbeddingEngine {
|
|
24
|
+
readonly info: EngineInfo;
|
|
25
|
+
/** What produced the vectors, recorded by an index next to them. */
|
|
26
|
+
readonly modelId: string;
|
|
27
|
+
readonly dimensions: number;
|
|
28
|
+
/** Where the vectors are computed right now, when the engine knows; it can change mid-run. */
|
|
29
|
+
readonly backend?: 'gpu' | 'cpu';
|
|
30
|
+
embedQuery(text: string, options?: EmbedCallOptions): Promise<Float32Array>;
|
|
31
|
+
embedDocuments(documents: readonly EmbedDocument[], options?: EmbedCallOptions): Promise<Float32Array[]>;
|
|
32
|
+
destroy(): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
export interface EngineCreateOptions {
|
|
35
|
+
readonly monitor?: CreateMonitorCallback;
|
|
36
|
+
readonly signal?: AbortSignal;
|
|
37
|
+
}
|
|
38
|
+
/** How `Embedder` asks an engine whether it can run, and starts it. */
|
|
39
|
+
export interface EmbeddingEngineFactory {
|
|
40
|
+
readonly info: EngineInfo;
|
|
41
|
+
availability(): Promise<Availability>;
|
|
42
|
+
create(options?: EngineCreateOptions): Promise<EmbeddingEngine>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type GpuSetting, type MachineProbe } from '@gjsify/machine-fit';
|
|
2
|
+
/** The build description node-llama-cpp writes beside every prebuilt binary. */
|
|
3
|
+
export declare const BUILD_METADATA_FILE = "_nlcBuildMetadata.json";
|
|
4
|
+
/** This machine, as node-llama-cpp names its binary directories. */
|
|
5
|
+
export declare function currentTarget(probe?: Pick<MachineProbe, 'platform' | 'arch'>): string;
|
|
6
|
+
/**
|
|
7
|
+
* Is there a prebuilt binary for this machine? A table lookup, so it is free
|
|
8
|
+
* to ask; `true` does not promise the model loads.
|
|
9
|
+
*
|
|
10
|
+
* Two answers, because the runtimes reach the binary differently: under GJS
|
|
11
|
+
* the engine hands node-llama-cpp a directory, under Node `getLlama()` looks
|
|
12
|
+
* for it. Answering from the GJS table on Node would rule out every Mac.
|
|
13
|
+
*/
|
|
14
|
+
export declare function hasPrebuiltBins(target?: string, onGjs?: boolean): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* The GJS builds to try, best first; the last is always the CPU build. Only
|
|
17
|
+
* Linux x64 has two. Not asking `getLlama()` is deliberate: it `fork`s a
|
|
18
|
+
* second interpreter to self-test the binary.
|
|
19
|
+
*/
|
|
20
|
+
export declare function backendCandidates(target: string, gpu: GpuSetting, probe: MachineProbe): string[];
|
|
21
|
+
/** The directory holding the addon and its backends, or an error naming the machine. */
|
|
22
|
+
export declare function prebuiltBinsDir(target: string): Promise<string>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type GpuPin, type GpuSetting, type MachineProbe } from '@gjsify/machine-fit';
|
|
2
|
+
import { ModelStore } from '@gjsify/model-store';
|
|
3
|
+
import type { EmbeddingEngineFactory, EngineInfo } from './engine.js';
|
|
4
|
+
import { type EmbeddingModelEntry } from './llama-models.js';
|
|
5
|
+
export declare const LLAMA_ENGINE: EngineInfo;
|
|
6
|
+
/** Two, not "all cores": an indexer may run for hours next to the person's own work. */
|
|
7
|
+
export declare const DEFAULT_THREADS = 2;
|
|
8
|
+
/**
|
|
9
|
+
* Contexts that evaluate side by side on a GPU. One context holds one
|
|
10
|
+
* sequence and node-llama-cpp serialises calls on it, so a lone context leaves
|
|
11
|
+
* the device idle between calls. Measured on an RX 6600, 400 documents in
|
|
12
|
+
* batches of 16: one context 24.6/s, two 27.5/s, four and six no more than
|
|
13
|
+
* two. The CPU keeps one: there the threads are the budget.
|
|
14
|
+
*/
|
|
15
|
+
export declare const DEFAULT_GPU_CONTEXTS = 2;
|
|
16
|
+
export interface LlamaEmbeddingsOptions {
|
|
17
|
+
/** An id from {@link EMBEDDING_MODELS}. */
|
|
18
|
+
readonly model?: string;
|
|
19
|
+
/** A file the person already has; checked against the manifest's size and digest. */
|
|
20
|
+
readonly modelPath?: string;
|
|
21
|
+
/** Directories the app ships models in, read-only, searched before the cache. */
|
|
22
|
+
readonly appDataDirs?: readonly string[];
|
|
23
|
+
/** A store of {@link EMBEDDING_MODELS}; tests and apps with their own cache root. */
|
|
24
|
+
readonly store?: ModelStore<EmbeddingModelEntry>;
|
|
25
|
+
/** One of the model's `truncations`. Default: its native size. */
|
|
26
|
+
readonly dimensions?: number;
|
|
27
|
+
/** The person's GPU switch, e.g. `parseGpuSetting(env.MYAPP_GPU)`. `on` also lifts a pin. */
|
|
28
|
+
readonly gpu?: GpuSetting;
|
|
29
|
+
readonly threads?: number;
|
|
30
|
+
readonly gpuContexts?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Where the GPU guard keeps its pin. Default: the one shared by every
|
|
33
|
+
* gjsify GPU engine (ADR 0132 § 6), so a hang under one app warns the next.
|
|
34
|
+
*/
|
|
35
|
+
readonly gpuGuardDir?: string;
|
|
36
|
+
/** Told once per process when a pin keeps this engine on the CPU. Default: a line on stderr. */
|
|
37
|
+
readonly onGpuPinned?: (pin: GpuPin) => void;
|
|
38
|
+
/** Run the GC after each `embedDocuments` on GJS; see `collectGarbage`. Default `true`. */
|
|
39
|
+
readonly collectGarbage?: boolean;
|
|
40
|
+
/** The machine `availability()` asks. */
|
|
41
|
+
readonly probe?: MachineProbe;
|
|
42
|
+
}
|
|
43
|
+
interface EmbeddingContextLike {
|
|
44
|
+
getEmbeddingFor(text: string): Promise<{
|
|
45
|
+
vector: readonly number[];
|
|
46
|
+
}>;
|
|
47
|
+
dispose(): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
interface ModelLike {
|
|
50
|
+
tokenize(text: string): readonly number[];
|
|
51
|
+
detokenize(tokens: readonly number[]): string;
|
|
52
|
+
createEmbeddingContext(options: {
|
|
53
|
+
contextSize: number;
|
|
54
|
+
threads?: number;
|
|
55
|
+
}): Promise<EmbeddingContextLike>;
|
|
56
|
+
dispose(): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
interface LlamaLike {
|
|
59
|
+
gpu: false | string;
|
|
60
|
+
loadModel(options: {
|
|
61
|
+
modelPath: string;
|
|
62
|
+
gpuLayers?: number;
|
|
63
|
+
}): Promise<ModelLike>;
|
|
64
|
+
dispose(): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
export interface NodeLlamaCpp {
|
|
67
|
+
getLlama(options: {
|
|
68
|
+
gpu: 'auto' | false;
|
|
69
|
+
build?: 'never';
|
|
70
|
+
}): Promise<LlamaLike>;
|
|
71
|
+
Llama: {
|
|
72
|
+
_create(options: Record<string, unknown>): Promise<LlamaLike>;
|
|
73
|
+
defaultConsoleLogger: unknown;
|
|
74
|
+
};
|
|
75
|
+
LlamaLogLevel: {
|
|
76
|
+
warn: unknown;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** {@link llamaEmbeddings} with node-llama-cpp passed in. */
|
|
80
|
+
export declare function createLlamaEmbeddings(nlc: NodeLlamaCpp, options?: LlamaEmbeddingsOptions): EmbeddingEngineFactory;
|
|
81
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ModelEntry } from '@gjsify/model-store';
|
|
2
|
+
export interface EmbeddingModelEntry extends ModelEntry {
|
|
3
|
+
/**
|
|
4
|
+
* What an index records next to the vectors (`Embedder.modelId`). Never
|
|
5
|
+
* changes for an entry: a new value makes every existing index stale.
|
|
6
|
+
*/
|
|
7
|
+
readonly modelId: string;
|
|
8
|
+
/** Native vector size. */
|
|
9
|
+
readonly dimensions: number;
|
|
10
|
+
/** Sizes the model was trained to be truncated to (Matryoshka), largest first. */
|
|
11
|
+
readonly truncations: readonly number[];
|
|
12
|
+
/** The query prompt; `{text}` is replaced. */
|
|
13
|
+
readonly queryTemplate: string;
|
|
14
|
+
/** The document prompt; `{title}` and `{text}` are replaced. */
|
|
15
|
+
readonly documentTemplate: string;
|
|
16
|
+
/** What `{title}` becomes for a document without one. */
|
|
17
|
+
readonly emptyTitle: string;
|
|
18
|
+
/** Tokens per context. Longer input is cut to fit (`fitTokens`). */
|
|
19
|
+
readonly contextTokens: number;
|
|
20
|
+
/**
|
|
21
|
+
* Memory one loaded copy needs, runtime included, for `checkFit`
|
|
22
|
+
* (ADR 0136 § 6). Measured, not derived from the file size.
|
|
23
|
+
*/
|
|
24
|
+
readonly footprintBytes: number;
|
|
25
|
+
}
|
|
26
|
+
export declare const DEFAULT_EMBEDDING_MODEL = "embeddinggemma-2-q8_0";
|
|
27
|
+
export declare const EMBEDDING_MODELS: readonly EmbeddingModelEntry[];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { EmbedDocument } from './engine.js';
|
|
2
|
+
import type { EmbeddingModelEntry } from './llama-models.js';
|
|
3
|
+
type Templates = Pick<EmbeddingModelEntry, 'queryTemplate' | 'documentTemplate' | 'emptyTitle'>;
|
|
4
|
+
export declare function queryPrompt(model: Templates, text: string): string;
|
|
5
|
+
/** The title goes into its own field only; repeating it in the text would weigh it twice. */
|
|
6
|
+
export declare function documentPrompt(model: Templates, document: EmbedDocument): string;
|
|
7
|
+
/** The two tokenizer calls {@link fitTokens} needs; a loaded model has both. */
|
|
8
|
+
export interface Tokenizing {
|
|
9
|
+
tokenize(text: string): readonly number[];
|
|
10
|
+
detokenize(tokens: readonly number[]): string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Cut the text to what a context of `contextTokens` holds. A character limit
|
|
14
|
+
* is only a guess: digits and rare scripts cost about one token per
|
|
15
|
+
* character, and llama.cpp rejects an over-long input instead of truncating.
|
|
16
|
+
*/
|
|
17
|
+
export declare function fitTokens(model: Tokenizing, text: string, contextTokens: number): string;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** What {@link runningOnGjs} looks at; injected so the Node-with-node-gi case is testable. */
|
|
2
|
+
export interface RuntimeHost {
|
|
3
|
+
imports?: unknown;
|
|
4
|
+
process?: {
|
|
5
|
+
versions?: {
|
|
6
|
+
gjs?: string;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Is this GJS? Not "is there an `imports`": node-gi defines
|
|
12
|
+
* `globalThis.imports` on Node too, and asking only that sent a Node app down
|
|
13
|
+
* the `@gjsify/napi` route, which Node cannot load.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runningOnGjs(host?: RuntimeHost): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Run SpiderMonkey's garbage collector now; a no-op on Node.
|
|
18
|
+
*
|
|
19
|
+
* GJS decides when to collect from the JS heap alone, and an embedding run's
|
|
20
|
+
* garbage is mostly NATIVE memory behind small wrapper objects. The heap looks
|
|
21
|
+
* small, so no collection comes: a 1500-document run climbed from 0.85 GB to
|
|
22
|
+
* 1.8 GB, and one collection per batch held it at 0.85 GB.
|
|
23
|
+
*
|
|
24
|
+
* A collection inside a database transaction has broken commits before. A
|
|
25
|
+
* caller that embeds inside one sets `collectGarbage: false` on the engine and
|
|
26
|
+
* calls this after the commit.
|
|
27
|
+
*/
|
|
28
|
+
export declare function collectGarbage(): void;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EmbeddingEngineFactory } from './engine.js';
|
|
2
|
+
import { type LlamaEmbeddingsOptions } from './llama-engine.js';
|
|
3
|
+
export { DEFAULT_GPU_CONTEXTS, DEFAULT_THREADS, LLAMA_ENGINE, type LlamaEmbeddingsOptions } from './llama-engine.js';
|
|
4
|
+
export { DEFAULT_EMBEDDING_MODEL, EMBEDDING_MODELS, type EmbeddingModelEntry } from './llama-models.js';
|
|
5
|
+
export { collectGarbage } from './llama-runtime.js';
|
|
6
|
+
/** The llama.cpp engine factory, for `Embedder.availability()` and `Embedder.create()`. */
|
|
7
|
+
export declare function llamaEmbeddings(options?: LlamaEmbeddingsOptions): EmbeddingEngineFactory;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface PoolResult<T> {
|
|
2
|
+
/** One slot per text; a slot is empty when its text was never embedded. */
|
|
3
|
+
vectors: (T | undefined)[];
|
|
4
|
+
/** The first error a context threw, if any. */
|
|
5
|
+
failure?: {
|
|
6
|
+
error: unknown;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Every context takes the next text until none is left, so one long text does
|
|
11
|
+
* not hold up the others. The first failure stops the workers from taking
|
|
12
|
+
* more, and the call returns only once EVERY worker has settled: the caller
|
|
13
|
+
* may then dispose the contexts, which is not safe under a running evaluation.
|
|
14
|
+
*/
|
|
15
|
+
export declare function embedAcross<C, T>(contexts: readonly C[], texts: readonly string[], embedOne: (context: C, text: string) => Promise<T>): Promise<PoolResult<T>>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Throws when `vector` is empty, holds a non-finite value, or is all zeros —
|
|
3
|
+
* a zero vector has no direction, so cosine similarity is undefined for it.
|
|
4
|
+
*/
|
|
5
|
+
export declare function checkVector(vector: ArrayLike<number>): void;
|
|
6
|
+
/** A unit-length copy. Throws on what {@link checkVector} rejects. */
|
|
7
|
+
export declare function normalize(vector: ArrayLike<number>): Float32Array;
|
|
8
|
+
/**
|
|
9
|
+
* Matryoshka truncation: keep the leading `dimensions` and renormalise. Only
|
|
10
|
+
* meaningful for a model trained for it; the model's entry lists the sizes.
|
|
11
|
+
*/
|
|
12
|
+
export declare function truncateVector(vector: ArrayLike<number>, dimensions: number): Float32Array;
|
package/package.json
CHANGED
|
@@ -1,6 +1,114 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@gjsify/embeddings",
|
|
3
|
+
"version": "0.64.26",
|
|
4
|
+
"description": "Text embeddings in the WebML CG lifecycle shape: an Embedder with availability(), create({ monitor, signal }) and destroy(), checked Float32Array vectors, and a local llama.cpp engine for GGUF models on Node and GJS.",
|
|
5
|
+
"module": "lib/esm/index.js",
|
|
6
|
+
"types": "lib/types/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./lib/types/index.d.ts",
|
|
12
|
+
"default": "./lib/esm/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./engine": {
|
|
15
|
+
"types": "./lib/types/engine.d.ts",
|
|
16
|
+
"default": "./lib/esm/engine.js"
|
|
17
|
+
},
|
|
18
|
+
"./llama": {
|
|
19
|
+
"types": "./lib/types/llama.d.ts",
|
|
20
|
+
"default": "./lib/esm/llama.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"lib",
|
|
25
|
+
"!lib/types/**/*.spec.d.ts"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"clear": "gjsify clear lib tmp tsconfig.tsbuildinfo test.gjs.mjs test.node.mjs",
|
|
29
|
+
"check": "gjsify tsc --noEmit",
|
|
30
|
+
"build": "gjsify run build:gjsify && gjsify run build:types",
|
|
31
|
+
"build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
|
|
32
|
+
"build:types": "gjsify tsc",
|
|
33
|
+
"build:test": "gjsify run build:test:gjs && gjsify run build:test:node",
|
|
34
|
+
"build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs --external node-llama-cpp,@node-llama-cpp/linux-x64,@node-llama-cpp/linux-x64-vulkan,@node-llama-cpp/linux-arm64,@gjsify/napi",
|
|
35
|
+
"build:test:node": "gjsify build src/test.mts --app node --outfile test.node.mjs --external node-llama-cpp,@node-llama-cpp/linux-x64,@node-llama-cpp/linux-x64-vulkan,@node-llama-cpp/linux-arm64,@gjsify/napi",
|
|
36
|
+
"test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:node && gjsify run test:gjs",
|
|
37
|
+
"test:gjs": "gjsify run test.gjs.mjs",
|
|
38
|
+
"test:node": "node test.node.mjs"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"embeddings",
|
|
42
|
+
"semantic-search",
|
|
43
|
+
"ai",
|
|
44
|
+
"gguf",
|
|
45
|
+
"llama.cpp",
|
|
46
|
+
"webml"
|
|
47
|
+
],
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@gjsify/fs": "^0.64.26",
|
|
50
|
+
"@gjsify/machine-fit": "^0.64.26",
|
|
51
|
+
"@gjsify/model-store": "^0.64.26",
|
|
52
|
+
"@gjsify/path": "^0.64.26",
|
|
53
|
+
"@gjsify/process": "^0.64.26"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@gjsify/napi": "^0.64.19",
|
|
57
|
+
"node-llama-cpp": "^3.22.2",
|
|
58
|
+
"@node-llama-cpp/linux-arm64": "^3.22.2",
|
|
59
|
+
"@node-llama-cpp/linux-x64": "^3.22.2",
|
|
60
|
+
"@node-llama-cpp/linux-x64-vulkan": "^3.22.2"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"@gjsify/napi": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"node-llama-cpp": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"@node-llama-cpp/linux-arm64": {
|
|
70
|
+
"optional": true
|
|
71
|
+
},
|
|
72
|
+
"@node-llama-cpp/linux-x64": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@node-llama-cpp/linux-x64-vulkan": {
|
|
76
|
+
"optional": true
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"@gjsify/cli": "^0.64.26",
|
|
81
|
+
"@gjsify/unit": "^0.64.26",
|
|
82
|
+
"@types/node": "^25.9.2",
|
|
83
|
+
"typescript": "^6.0.3"
|
|
84
|
+
},
|
|
85
|
+
"license": "MIT",
|
|
86
|
+
"repository": {
|
|
87
|
+
"type": "git",
|
|
88
|
+
"url": "git+https://github.com/gjsify/gjsify.git",
|
|
89
|
+
"directory": "packages/gjs/embeddings"
|
|
90
|
+
},
|
|
91
|
+
"bugs": {
|
|
92
|
+
"url": "https://github.com/gjsify/gjsify/issues"
|
|
93
|
+
},
|
|
94
|
+
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/gjs/embeddings#readme",
|
|
95
|
+
"gjsify": {
|
|
96
|
+
"tier": 3,
|
|
97
|
+
"runtimes": {
|
|
98
|
+
"gjs": "polyfill",
|
|
99
|
+
"node": "none",
|
|
100
|
+
"browser": "none",
|
|
101
|
+
"nativescript": "none"
|
|
102
|
+
},
|
|
103
|
+
"os": {
|
|
104
|
+
"linux": "supported",
|
|
105
|
+
"darwin": "partial",
|
|
106
|
+
"win32": "partial"
|
|
107
|
+
},
|
|
108
|
+
"osNotes": {
|
|
109
|
+
"darwin": "The llama.cpp engine runs on Node only: the GJS route loads node-llama-cpp's Linux prebuilt binaries.",
|
|
110
|
+
"win32": "The llama.cpp engine runs on Node only: the GJS route loads node-llama-cpp's Linux prebuilt binaries."
|
|
111
|
+
},
|
|
112
|
+
"headless": true
|
|
113
|
+
}
|
|
114
|
+
}
|