@plur-ai/core 0.2.4 → 0.2.6

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/index.d.ts CHANGED
@@ -769,8 +769,11 @@ declare class Plur {
769
769
  private _filterEngrams;
770
770
  /** Reactivate accessed engrams (bump retrieval strength, frequency, last_accessed) */
771
771
  private _reactivateResults;
772
- /** Scored injection within token budget. Returns formatted strings. */
772
+ /** Scored injection within token budget (BM25 only). Returns formatted strings. */
773
773
  inject(task: string, options?: InjectOptions): InjectionResult;
774
+ /** Scored injection with embedding boost when available. Falls back to BM25 if embeddings not installed. */
775
+ injectHybrid(task: string, options?: InjectOptions): Promise<InjectionResult>;
776
+ private _formatInjection;
774
777
  /** Update feedback_signals and adjust retrieval_strength. */
775
778
  feedback(id: string, signal: 'positive' | 'negative' | 'neutral'): void;
776
779
  /** Set engram status to 'retired'. */
package/dist/index.js CHANGED
@@ -509,7 +509,7 @@ function fillTokenBudget(scored, maxTokens) {
509
509
  }
510
510
  return { selected: result, tokens_used: tokensUsed };
511
511
  }
512
- function selectAndSpread(ctx, personalEngrams, packs, config) {
512
+ function selectAndSpread(ctx, personalEngrams, packs, config, embeddingBoosts) {
513
513
  const spreadCap = config?.spread_cap ?? 3;
514
514
  const spreadBudget = config?.spread_budget ?? 480;
515
515
  const promptLower = ctx.prompt.toLowerCase();
@@ -521,7 +521,13 @@ function selectAndSpread(ctx, personalEngrams, packs, config) {
521
521
  for (const engram of personalEngrams) {
522
522
  if (engram.status !== "active") continue;
523
523
  engramMap.set(engram.id, engram);
524
- const raw = scoreEngram(engram, promptLower, promptWords, [], ctx.scope, false);
524
+ let raw = scoreEngram(engram, promptLower, promptWords, [], ctx.scope, false);
525
+ const embBoost = embeddingBoosts?.get(engram.id) ?? 0;
526
+ if (raw === 0 && embBoost > 0.3) {
527
+ raw = embBoost * 2;
528
+ } else if (raw > 0 && embBoost > 0) {
529
+ raw += embBoost;
530
+ }
525
531
  if (raw > 0) {
526
532
  scored.push({ ...engram, keyword_match: raw, raw_score: raw, score: raw });
527
533
  }
@@ -533,7 +539,13 @@ function selectAndSpread(ctx, personalEngrams, packs, config) {
533
539
  for (const engram of pack.engrams) {
534
540
  if (engram.status !== "active") continue;
535
541
  engramMap.set(engram.id, engram);
536
- const raw = scoreEngram(engram, promptLower, promptWords, matchTerms, ctx.scope, true);
542
+ let raw = scoreEngram(engram, promptLower, promptWords, matchTerms, ctx.scope, true);
543
+ const embBoost = embeddingBoosts?.get(engram.id) ?? 0;
544
+ if (raw === 0 && embBoost > 0.3) {
545
+ raw = embBoost * 2;
546
+ } else if (raw > 0 && embBoost > 0) {
547
+ raw += embBoost;
548
+ }
537
549
  if (raw > 0) {
538
550
  scored.push({ ...engram, keyword_match: raw, raw_score: raw, score: raw });
539
551
  }
@@ -732,17 +744,25 @@ Rules:
732
744
  import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
733
745
  import { join as join2 } from "path";
734
746
  var embedPipeline = null;
747
+ var transformersUnavailable = false;
735
748
  async function getEmbedder() {
749
+ if (transformersUnavailable) return null;
736
750
  if (!embedPipeline) {
737
- const { pipeline } = await import("@huggingface/transformers");
738
- embedPipeline = await pipeline("feature-extraction", "Xenova/bge-small-en-v1.5", {
739
- dtype: "fp32"
740
- });
751
+ try {
752
+ const { pipeline } = await import("@huggingface/transformers");
753
+ embedPipeline = await pipeline("feature-extraction", "Xenova/bge-small-en-v1.5", {
754
+ dtype: "fp32"
755
+ });
756
+ } catch {
757
+ transformersUnavailable = true;
758
+ return null;
759
+ }
741
760
  }
742
761
  return embedPipeline;
743
762
  }
744
763
  async function embed(text) {
745
764
  const embedder = await getEmbedder();
765
+ if (!embedder) return null;
746
766
  const result = await embedder(text, { pooling: "cls", normalize: true });
747
767
  return new Float32Array(result.data);
748
768
  }
@@ -776,6 +796,9 @@ async function embeddingSearch(engrams, query, limit, storagePath) {
776
796
  const cachePath = storagePath ? join2(storagePath, ".embeddings-cache.json") : ".embeddings-cache.json";
777
797
  const cache2 = loadCache(cachePath);
778
798
  const queryEmbedding = await embed(query);
799
+ if (!queryEmbedding) {
800
+ return [];
801
+ }
779
802
  const similarities = [];
780
803
  for (const engram of engrams) {
781
804
  const searchText = engramSearchText(engram);
@@ -784,7 +807,9 @@ async function embeddingSearch(engrams, query, limit, storagePath) {
784
807
  if (cache2[engram.id]?.hash === hash) {
785
808
  engramEmbedding = new Float32Array(cache2[engram.id].embedding);
786
809
  } else {
787
- engramEmbedding = await embed(searchText);
810
+ const emb = await embed(searchText);
811
+ if (!emb) return [];
812
+ engramEmbedding = emb;
788
813
  cache2[engram.id] = {
789
814
  hash,
790
815
  embedding: Array.from(engramEmbedding)
@@ -1331,8 +1356,27 @@ var Plur = class {
1331
1356
  }
1332
1357
  if (modified) saveEngrams(this.paths.engrams, allEngrams);
1333
1358
  }
1334
- /** Scored injection within token budget. Returns formatted strings. */
1359
+ /** Scored injection within token budget (BM25 only). Returns formatted strings. */
1335
1360
  inject(task, options) {
1361
+ return this._formatInjection(task, options);
1362
+ }
1363
+ /** Scored injection with embedding boost when available. Falls back to BM25 if embeddings not installed. */
1364
+ async injectHybrid(task, options) {
1365
+ let embeddingBoosts;
1366
+ try {
1367
+ const engrams = loadEngrams(this.paths.engrams).filter((e) => e.status === "active");
1368
+ const results = await embeddingSearch(engrams, task, engrams.length, this.paths.root);
1369
+ if (results.length > 0) {
1370
+ embeddingBoosts = /* @__PURE__ */ new Map();
1371
+ for (let i = 0; i < results.length; i++) {
1372
+ embeddingBoosts.set(results[i].id, 1 / (1 + i * 0.1));
1373
+ }
1374
+ }
1375
+ } catch {
1376
+ }
1377
+ return this._formatInjection(task, options, embeddingBoosts);
1378
+ }
1379
+ _formatInjection(task, options, embeddingBoosts) {
1336
1380
  const engrams = loadEngrams(this.paths.engrams);
1337
1381
  const packs = loadAllPacks(this.paths.packs);
1338
1382
  const budget = options?.budget ?? this.config.injection_budget ?? 2e3;
@@ -1347,7 +1391,8 @@ var Plur = class {
1347
1391
  {
1348
1392
  spread_cap: this.config.injection?.spread_cap,
1349
1393
  spread_budget: this.config.injection?.spread_budget
1350
- }
1394
+ },
1395
+ embeddingBoosts
1351
1396
  );
1352
1397
  const formatEngrams = (wires) => {
1353
1398
  if (wires.length === 0) return "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plur-ai/core",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,7 +8,6 @@
8
8
  "dist"
9
9
  ],
10
10
  "dependencies": {
11
- "@huggingface/transformers": "^3.8.1",
12
11
  "js-yaml": "^4.1.0",
13
12
  "zod": "^3.23.0"
14
13
  },