1+ import os from "node:os" ;
2+ import path from "node:path" ;
13import {
24 createPluginRegistryFixture ,
35 registerVirtualTestPlugin ,
@@ -21,7 +23,9 @@ import llamaCppPlugin from "./index.js";
2123import {
2224 DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ,
2325 createLlamaCppEmbeddingProvider ,
26+ createLlamaCppMemoryEmbeddingProvider ,
2427 formatLlamaCppSetupError ,
28+ llamaCppEmbeddingProviderAdapter ,
2529} from "./src/embedding-provider.js" ;
2630
2731afterEach ( ( ) => {
@@ -106,6 +110,285 @@ describe("llama.cpp provider plugin", () => {
106110 } ) ;
107111 } ) ;
108112
113+ it ( "keeps the default model identity when configured with its exact cache artifact path" , async ( ) => {
114+ const modelPath = path . join (
115+ os . homedir ( ) ,
116+ ".node-llama-cpp" ,
117+ "models" ,
118+ "hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf" ,
119+ ) ;
120+ memoryHostEmbeddingMocks . createLocalEmbeddingProvider . mockResolvedValue ( {
121+ id : "local" ,
122+ model : modelPath ,
123+ embedQuery : vi . fn ( ) ,
124+ embedBatch : vi . fn ( ) ,
125+ } ) ;
126+
127+ const result = await createLlamaCppMemoryEmbeddingProvider (
128+ {
129+ config : { } ,
130+ provider : "local" ,
131+ fallback : "none" ,
132+ model : modelPath ,
133+ local : { modelPath } ,
134+ } ,
135+ { nodeLlamaCppImportUrl : "file:///plugin/node-llama-cpp.js" } ,
136+ ) ;
137+
138+ expect ( result . provider ?. model ) . toBe ( DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ) ;
139+ expect ( result . runtime ?. cacheKeyData ) . toEqual ( {
140+ provider : "local" ,
141+ model : DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ,
142+ } ) ;
143+ expect ( result . runtime ?. indexIdentityAliases ) . toEqual ( [
144+ {
145+ model : modelPath ,
146+ cacheKeyData : {
147+ provider : "local" ,
148+ model : modelPath ,
149+ } ,
150+ } ,
151+ {
152+ model : "hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf" ,
153+ cacheKeyData : {
154+ provider : "local" ,
155+ model : "hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf" ,
156+ } ,
157+ } ,
158+ ] ) ;
159+ expect (
160+ llamaCppEmbeddingProviderAdapter . resolveIndexIdentity ?.( {
161+ config : { } ,
162+ provider : "local" ,
163+ model : modelPath ,
164+ local : { modelPath } ,
165+ } ) ,
166+ ) . toEqual ( {
167+ model : DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ,
168+ cacheKeyData : {
169+ provider : "local" ,
170+ model : DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ,
171+ } ,
172+ aliases : [
173+ {
174+ model : modelPath ,
175+ cacheKeyData : {
176+ provider : "local" ,
177+ model : modelPath ,
178+ } ,
179+ } ,
180+ {
181+ model : "hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf" ,
182+ cacheKeyData : {
183+ provider : "local" ,
184+ model : "hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf" ,
185+ } ,
186+ } ,
187+ ] ,
188+ } ) ;
189+ expect ( memoryHostEmbeddingMocks . createLocalEmbeddingProvider ) . toHaveBeenCalledWith (
190+ expect . objectContaining ( {
191+ model : modelPath ,
192+ local : { modelPath } ,
193+ } ) ,
194+ {
195+ nodeLlamaCppImportUrl : "file:///plugin/node-llama-cpp.js" ,
196+ } ,
197+ ) ;
198+ } ) ;
199+
200+ it ( "keeps an arbitrary same-basename model path as a distinct identity" , async ( ) => {
201+ const modelPath = path . join (
202+ os . tmpdir ( ) ,
203+ "custom-models" ,
204+ DEFAULT_LLAMA_CPP_EMBEDDING_MODEL . split ( "/" ) . at ( - 1 ) ! ,
205+ ) ;
206+ memoryHostEmbeddingMocks . createLocalEmbeddingProvider . mockResolvedValue ( {
207+ id : "local" ,
208+ model : modelPath ,
209+ embedQuery : vi . fn ( ) ,
210+ embedBatch : vi . fn ( ) ,
211+ } ) ;
212+
213+ const result = await createLlamaCppMemoryEmbeddingProvider (
214+ {
215+ config : { } ,
216+ provider : "local" ,
217+ fallback : "none" ,
218+ model : modelPath ,
219+ local : { modelPath } ,
220+ } ,
221+ { nodeLlamaCppImportUrl : "file:///plugin/node-llama-cpp.js" } ,
222+ ) ;
223+
224+ expect ( result . provider ?. model ) . toBe ( modelPath ) ;
225+ expect ( result . runtime ?. cacheKeyData ) . toEqual ( {
226+ provider : "local" ,
227+ model : modelPath ,
228+ } ) ;
229+ expect ( result . runtime ) . not . toHaveProperty ( "indexIdentityAliases" ) ;
230+ } ) ;
231+
232+ it ( "keeps a bare same-basename file in the default cache as a distinct identity" , async ( ) => {
233+ const modelPath = path . join (
234+ os . homedir ( ) ,
235+ ".node-llama-cpp" ,
236+ "models" ,
237+ DEFAULT_LLAMA_CPP_EMBEDDING_MODEL . split ( "/" ) . at ( - 1 ) ! ,
238+ ) ;
239+ memoryHostEmbeddingMocks . createLocalEmbeddingProvider . mockResolvedValue ( {
240+ id : "local" ,
241+ model : modelPath ,
242+ embedQuery : vi . fn ( ) ,
243+ embedBatch : vi . fn ( ) ,
244+ } ) ;
245+
246+ const result = await createLlamaCppMemoryEmbeddingProvider (
247+ {
248+ config : { } ,
249+ provider : "local" ,
250+ fallback : "none" ,
251+ model : modelPath ,
252+ local : { modelPath } ,
253+ } ,
254+ { nodeLlamaCppImportUrl : "file:///plugin/node-llama-cpp.js" } ,
255+ ) ;
256+
257+ expect ( result . provider ?. model ) . toBe ( modelPath ) ;
258+ expect ( result . runtime ) . not . toHaveProperty ( "indexIdentityAliases" ) ;
259+ } ) ;
260+
261+ it ( "keeps the default model identity with a custom cache directory" , async ( ) => {
262+ const modelCacheDir = path . join ( os . tmpdir ( ) , "llama-cpp-model-cache" ) ;
263+ const modelPath = path . join ( modelCacheDir , "hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf" ) ;
264+ memoryHostEmbeddingMocks . createLocalEmbeddingProvider . mockResolvedValue ( {
265+ id : "local" ,
266+ model : modelPath ,
267+ embedQuery : vi . fn ( ) ,
268+ embedBatch : vi . fn ( ) ,
269+ } ) ;
270+
271+ const result = await createLlamaCppMemoryEmbeddingProvider (
272+ {
273+ config : { } ,
274+ provider : "local" ,
275+ fallback : "none" ,
276+ model : DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ,
277+ local : { modelPath : DEFAULT_LLAMA_CPP_EMBEDDING_MODEL , modelCacheDir } ,
278+ } ,
279+ { nodeLlamaCppImportUrl : "file:///plugin/node-llama-cpp.js" } ,
280+ ) ;
281+
282+ expect ( result . provider ?. model ) . toBe ( DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ) ;
283+ expect ( result . runtime ?. cacheKeyData ) . toEqual ( {
284+ provider : "local" ,
285+ model : DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ,
286+ } ) ;
287+ expect ( result . runtime ?. indexIdentityAliases ) . toEqual ( [
288+ {
289+ model : modelPath ,
290+ cacheKeyData : {
291+ provider : "local" ,
292+ model : modelPath ,
293+ } ,
294+ } ,
295+ {
296+ model : "hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf" ,
297+ cacheKeyData : {
298+ provider : "local" ,
299+ model : "hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf" ,
300+ } ,
301+ } ,
302+ ] ) ;
303+ } ) ;
304+
305+ it . each ( [
306+ {
307+ direction : "default URI to exact relative cache artifact" ,
308+ modelPath : DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ,
309+ } ,
310+ {
311+ direction : "exact relative cache artifact to default URI" ,
312+ modelPath : "hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf" ,
313+ } ,
314+ ] ) ( "keeps $direction compatible" , ( { modelPath } ) => {
315+ const modelCacheDir = path . join ( os . tmpdir ( ) , "llama-cpp-relative-model-cache" ) ;
316+ const relativeModelPath = "hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf" ;
317+ const resolvedModelPath = path . join ( modelCacheDir , relativeModelPath ) ;
318+
319+ expect (
320+ llamaCppEmbeddingProviderAdapter . resolveIndexIdentity ?.( {
321+ config : { } ,
322+ provider : "local" ,
323+ model : modelPath ,
324+ local : { modelPath, modelCacheDir } ,
325+ } ) ,
326+ ) . toEqual ( {
327+ model : DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ,
328+ cacheKeyData : {
329+ provider : "local" ,
330+ model : DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ,
331+ } ,
332+ aliases : [
333+ {
334+ model : resolvedModelPath ,
335+ cacheKeyData : {
336+ provider : "local" ,
337+ model : resolvedModelPath ,
338+ } ,
339+ } ,
340+ {
341+ model : relativeModelPath ,
342+ cacheKeyData : {
343+ provider : "local" ,
344+ model : relativeModelPath ,
345+ } ,
346+ } ,
347+ ] ,
348+ } ) ;
349+ } ) ;
350+
351+ it ( "keeps the default model identity for its exact relative cache artifact" , async ( ) => {
352+ const modelCacheDir = path . join ( os . tmpdir ( ) , "llama-cpp-relative-model-cache" ) ;
353+ const modelPath = "hf_ggml-org_embeddinggemma-300m-qat-Q8_0.gguf" ;
354+ const resolvedModelPath = path . join ( modelCacheDir , modelPath ) ;
355+ memoryHostEmbeddingMocks . createLocalEmbeddingProvider . mockResolvedValue ( {
356+ id : "local" ,
357+ model : modelPath ,
358+ embedQuery : vi . fn ( ) ,
359+ embedBatch : vi . fn ( ) ,
360+ } ) ;
361+
362+ const result = await createLlamaCppMemoryEmbeddingProvider (
363+ {
364+ config : { } ,
365+ provider : "local" ,
366+ fallback : "none" ,
367+ model : modelPath ,
368+ local : { modelPath, modelCacheDir } ,
369+ } ,
370+ { nodeLlamaCppImportUrl : "file:///plugin/node-llama-cpp.js" } ,
371+ ) ;
372+
373+ expect ( result . provider ?. model ) . toBe ( DEFAULT_LLAMA_CPP_EMBEDDING_MODEL ) ;
374+ expect ( result . runtime ?. indexIdentityAliases ) . toEqual ( [
375+ {
376+ model : resolvedModelPath ,
377+ cacheKeyData : {
378+ provider : "local" ,
379+ model : resolvedModelPath ,
380+ } ,
381+ } ,
382+ {
383+ model : modelPath ,
384+ cacheKeyData : {
385+ provider : "local" ,
386+ model : modelPath ,
387+ } ,
388+ } ,
389+ ] ) ;
390+ } ) ;
391+
109392 it ( "formats missing runtime errors with the plugin install command" , ( ) => {
110393 const err = Object . assign ( new Error ( "Cannot find package 'node-llama-cpp'" ) , {
111394 code : "ERR_MODULE_NOT_FOUND" ,
0 commit comments