@dudousxd/nestjs-agent-rag 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -3
- package/dist/index.cjs +43 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -2
- package/dist/index.d.ts +81 -2
- package/dist/index.js +43 -2
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/index.d.cts
CHANGED
|
@@ -5,6 +5,31 @@ interface ChunkOptions {
|
|
|
5
5
|
chunkSize?: number;
|
|
6
6
|
/** Characters of overlap carried from the end of one chunk into the next. Default 100. */
|
|
7
7
|
overlap?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Treat the text as records joined by this string, and never split one across chunks.
|
|
10
|
+
*
|
|
11
|
+
* Default chunking is structure-blind: it breaks at the *latest* paragraph/sentence/word boundary
|
|
12
|
+
* in the window, which for record-shaped text means a cut lands mid-record routinely. That is fine
|
|
13
|
+
* for prose, where the two halves are still sentences. It is not fine for a table flattened to one
|
|
14
|
+
* record per row, because the half that keeps the row identifier and the half that keeps the value
|
|
15
|
+
* end up in different chunks, and neither can answer a question about that row on its own.
|
|
16
|
+
*
|
|
17
|
+
* With a separator set, whole records are packed greedily up to `chunkSize` and a record is never
|
|
18
|
+
* cut — except one longer than `chunkSize`, which has nowhere to go and is character-split as
|
|
19
|
+
* usual, since no chunker can avoid that.
|
|
20
|
+
*
|
|
21
|
+
* `overlap` is IGNORED in this mode, deliberately. Overlap exists to rescue a sentence that a
|
|
22
|
+
* boundary cut in half; a boundary that never falls inside a record has nothing to rescue, and
|
|
23
|
+
* carrying the tail of the previous chunk in would duplicate whole records into neighbouring chunks
|
|
24
|
+
* — paying for the same rows twice at embedding time and letting one row match from two places.
|
|
25
|
+
*
|
|
26
|
+
* Measured on a 200-row spreadsheet flattened to one field-labelled record per row (15 columns,
|
|
27
|
+
* ~800-char budget): blind chunking left 27% of records split, and on questions targeting a field
|
|
28
|
+
* on the far side of such a cut, BM25 answered 54/66 (MRR 0.818) against 66/66 (MRR 1.000) with
|
|
29
|
+
* records kept whole. The dense leg scored 7/66 either way — 200 near-identical rows produce
|
|
30
|
+
* near-identical vectors, so that leg cannot do row lookup at all and chunking does not change it.
|
|
31
|
+
*/
|
|
32
|
+
separator?: string;
|
|
8
33
|
}
|
|
9
34
|
/**
|
|
10
35
|
* Splits text into overlapping chunks for embedding. Greedy up to `chunkSize`, but prefers to break
|
|
@@ -595,6 +620,29 @@ declare class RedisVectorStore implements LexicalVectorStore {
|
|
|
595
620
|
searchText(query: string, options: VectorSearchOptions): Promise<Passage[]>;
|
|
596
621
|
}
|
|
597
622
|
|
|
623
|
+
interface EmbeddingRetrieverOptions {
|
|
624
|
+
/**
|
|
625
|
+
* Drop passages scoring below this, so a query the corpus cannot answer returns nothing.
|
|
626
|
+
*
|
|
627
|
+
* A vector search always returns its `topK` nearest neighbours, and "nearest" is not "relevant".
|
|
628
|
+
* Ask a corpus of maintenance policy about the dining hall menu and it hands back the five least
|
|
629
|
+
* unrelated paragraphs it has, each with a score, in confident rank order. Nothing in that result
|
|
630
|
+
* says the answer is absent, so without a floor the consumer either feeds a model five irrelevant
|
|
631
|
+
* passages or invents a threshold of its own.
|
|
632
|
+
*
|
|
633
|
+
* **This is the only place in the package where a threshold is meaningful, which is why it lives
|
|
634
|
+
* here and not on `HybridRetriever`.** The score here comes straight from the store's vector
|
|
635
|
+
* search: a calibrated similarity, comparable across queries and monotonic in how well the passage
|
|
636
|
+
* matches. A fused score is neither — see the score section on `HybridRetriever`. Threshold each
|
|
637
|
+
* leg, then fuse; never the other way round.
|
|
638
|
+
*
|
|
639
|
+
* Pick the number by measuring, not by taste: score questions you know the corpus answers against
|
|
640
|
+
* questions you know it does not, and put the floor in the gap between the two populations. It is
|
|
641
|
+
* specific to the embedding model and the distance metric, so it does not survive changing either.
|
|
642
|
+
* Left unset nothing is dropped, which is the previous behaviour exactly.
|
|
643
|
+
*/
|
|
644
|
+
minScore?: number;
|
|
645
|
+
}
|
|
598
646
|
/**
|
|
599
647
|
* Bridges an {@link EmbeddingProvider} + {@link VectorStore} into the core {@link Retriever} SPI:
|
|
600
648
|
* embed the query, then vector-search. This is what you wire as the agent's retriever (as a tool
|
|
@@ -603,7 +651,8 @@ declare class RedisVectorStore implements LexicalVectorStore {
|
|
|
603
651
|
declare class EmbeddingRetriever implements Retriever {
|
|
604
652
|
private readonly embedder;
|
|
605
653
|
private readonly store;
|
|
606
|
-
|
|
654
|
+
private readonly options;
|
|
655
|
+
constructor(embedder: EmbeddingProvider, store: VectorStore, options?: EmbeddingRetrieverOptions);
|
|
607
656
|
retrieve(query: string, options?: RetrieveOptions): Promise<Passage[]>;
|
|
608
657
|
}
|
|
609
658
|
|
|
@@ -724,6 +773,36 @@ interface HybridRetrieverOptions {
|
|
|
724
773
|
* vector search with lexical BM25. RRF scores each passage by `Σ weight / (k + rank)` across the
|
|
725
774
|
* lists it appears in, so it needs no score normalization between incompatible scales (cosine vs
|
|
726
775
|
* BM25). Deduplicates by passage id, so aligned chunk ids from the two retrievers reinforce.
|
|
776
|
+
*
|
|
777
|
+
* ## The score on a fused passage is not a similarity. Do not threshold it.
|
|
778
|
+
*
|
|
779
|
+
* Read this before using `passage.score` for anything other than the order it already produced.
|
|
780
|
+
*
|
|
781
|
+
* Not normalizing between scales is what makes RRF robust, and the price is that the number it
|
|
782
|
+
* emits is a function of **rank alone** — it never sees a similarity at all. Two consequences that
|
|
783
|
+
* bite in practice:
|
|
784
|
+
*
|
|
785
|
+
* - **It is not comparable to the score on a passage from {@link EmbeddingRetriever}**, even though
|
|
786
|
+
* it arrives in the same field, of the same type, from the same `Retriever` interface. There is no
|
|
787
|
+
* discriminator; a consumer holding a `Passage[]` cannot tell which kind of number it has. If you
|
|
788
|
+
* surface scores to a user or a model, know which retriever produced them.
|
|
789
|
+
* - **Its range is narrow, fixed, and says nothing about quality.** Every possible score lies in
|
|
790
|
+
* `[minWeight / (k + fetchTopK), Σ weights / (k + 1)]` — with the defaults (`k = 60`,
|
|
791
|
+
* `fetchTopK = 20`, two unweighted legs) that is `[0.0125, 0.0328]`. A perfect match and the
|
|
792
|
+
* least-bad member of a set of terrible matches both come back around `0.016`.
|
|
793
|
+
*
|
|
794
|
+
* The second point is why a relevance floor cannot live here. What the fused score actually measures
|
|
795
|
+
* is how strongly the legs *agree* — and agreement tracks nothing about correctness. When both legs
|
|
796
|
+
* return the same nearest nothing for an unanswerable query, they reinforce it to the ceiling, the
|
|
797
|
+
* same ceiling a unanimous correct answer gets. Measured on one corpus, the dense leg alone
|
|
798
|
+
* separated cleanly (weakest real answer 0.244, loudest absent-topic answer 0.143) while those same
|
|
799
|
+
* queries after fusion scored 0.032 and 0.033 — inverted. On another corpus the inversion may not
|
|
800
|
+
* appear; what does not change is that both numbers live in a band roughly one part in fifty wide,
|
|
801
|
+
* with no room for a threshold to mean anything.
|
|
802
|
+
*
|
|
803
|
+
* So: put the floor on the legs, with {@link EmbeddingRetrieverOptions.minScore}, and fuse what
|
|
804
|
+
* survives. `HybridRetriever` deliberately has no `minScore` of its own; a threshold option here
|
|
805
|
+
* would be an invitation to apply it to a number that cannot support one.
|
|
727
806
|
*/
|
|
728
807
|
declare class HybridRetriever implements Retriever {
|
|
729
808
|
private readonly retrievers;
|
|
@@ -789,4 +868,4 @@ interface RetrievalTool {
|
|
|
789
868
|
*/
|
|
790
869
|
declare function createRetrievalTool(retriever: Retriever, options?: RetrievalToolOptions): RetrievalTool;
|
|
791
870
|
|
|
792
|
-
export { type ChunkOptions, type ChunkRecord, EmbeddingRetriever, FilteredRetriever, HybridRetriever, type HybridRetrieverOptions, type IndexedDocument, type IngestChunksOptions, type IngestDocument, type IngestOptions, KeywordRetriever, type KeywordRetrieverOptions, LexicalRetriever, type LexicalVectorStore, MemoryVectorStore, type MetadataPatch, type PgClient, PgVectorStore, type PgVectorStoreOptions, type RedisSearchClient, RedisVectorSchemaMismatchError, RedisVectorStore, type RedisVectorStoreOptions, RerankingRetriever, type RerankingRetrieverOptions, type RetrievalTool, type RetrievalToolOptions, UnsafeRemovalError, type VectorRecord, type VectorSearchOptions, type VectorStore, applyMetadataPatch, chunkDocuments, chunkText, createRetrievalTool, documentIdOf, ingestChunks, ingestDocuments, isLexicalVectorStore };
|
|
871
|
+
export { type ChunkOptions, type ChunkRecord, EmbeddingRetriever, type EmbeddingRetrieverOptions, FilteredRetriever, HybridRetriever, type HybridRetrieverOptions, type IndexedDocument, type IngestChunksOptions, type IngestDocument, type IngestOptions, KeywordRetriever, type KeywordRetrieverOptions, LexicalRetriever, type LexicalVectorStore, MemoryVectorStore, type MetadataPatch, type PgClient, PgVectorStore, type PgVectorStoreOptions, type RedisSearchClient, RedisVectorSchemaMismatchError, RedisVectorStore, type RedisVectorStoreOptions, RerankingRetriever, type RerankingRetrieverOptions, type RetrievalTool, type RetrievalToolOptions, UnsafeRemovalError, type VectorRecord, type VectorSearchOptions, type VectorStore, applyMetadataPatch, chunkDocuments, chunkText, createRetrievalTool, documentIdOf, ingestChunks, ingestDocuments, isLexicalVectorStore };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,31 @@ interface ChunkOptions {
|
|
|
5
5
|
chunkSize?: number;
|
|
6
6
|
/** Characters of overlap carried from the end of one chunk into the next. Default 100. */
|
|
7
7
|
overlap?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Treat the text as records joined by this string, and never split one across chunks.
|
|
10
|
+
*
|
|
11
|
+
* Default chunking is structure-blind: it breaks at the *latest* paragraph/sentence/word boundary
|
|
12
|
+
* in the window, which for record-shaped text means a cut lands mid-record routinely. That is fine
|
|
13
|
+
* for prose, where the two halves are still sentences. It is not fine for a table flattened to one
|
|
14
|
+
* record per row, because the half that keeps the row identifier and the half that keeps the value
|
|
15
|
+
* end up in different chunks, and neither can answer a question about that row on its own.
|
|
16
|
+
*
|
|
17
|
+
* With a separator set, whole records are packed greedily up to `chunkSize` and a record is never
|
|
18
|
+
* cut — except one longer than `chunkSize`, which has nowhere to go and is character-split as
|
|
19
|
+
* usual, since no chunker can avoid that.
|
|
20
|
+
*
|
|
21
|
+
* `overlap` is IGNORED in this mode, deliberately. Overlap exists to rescue a sentence that a
|
|
22
|
+
* boundary cut in half; a boundary that never falls inside a record has nothing to rescue, and
|
|
23
|
+
* carrying the tail of the previous chunk in would duplicate whole records into neighbouring chunks
|
|
24
|
+
* — paying for the same rows twice at embedding time and letting one row match from two places.
|
|
25
|
+
*
|
|
26
|
+
* Measured on a 200-row spreadsheet flattened to one field-labelled record per row (15 columns,
|
|
27
|
+
* ~800-char budget): blind chunking left 27% of records split, and on questions targeting a field
|
|
28
|
+
* on the far side of such a cut, BM25 answered 54/66 (MRR 0.818) against 66/66 (MRR 1.000) with
|
|
29
|
+
* records kept whole. The dense leg scored 7/66 either way — 200 near-identical rows produce
|
|
30
|
+
* near-identical vectors, so that leg cannot do row lookup at all and chunking does not change it.
|
|
31
|
+
*/
|
|
32
|
+
separator?: string;
|
|
8
33
|
}
|
|
9
34
|
/**
|
|
10
35
|
* Splits text into overlapping chunks for embedding. Greedy up to `chunkSize`, but prefers to break
|
|
@@ -595,6 +620,29 @@ declare class RedisVectorStore implements LexicalVectorStore {
|
|
|
595
620
|
searchText(query: string, options: VectorSearchOptions): Promise<Passage[]>;
|
|
596
621
|
}
|
|
597
622
|
|
|
623
|
+
interface EmbeddingRetrieverOptions {
|
|
624
|
+
/**
|
|
625
|
+
* Drop passages scoring below this, so a query the corpus cannot answer returns nothing.
|
|
626
|
+
*
|
|
627
|
+
* A vector search always returns its `topK` nearest neighbours, and "nearest" is not "relevant".
|
|
628
|
+
* Ask a corpus of maintenance policy about the dining hall menu and it hands back the five least
|
|
629
|
+
* unrelated paragraphs it has, each with a score, in confident rank order. Nothing in that result
|
|
630
|
+
* says the answer is absent, so without a floor the consumer either feeds a model five irrelevant
|
|
631
|
+
* passages or invents a threshold of its own.
|
|
632
|
+
*
|
|
633
|
+
* **This is the only place in the package where a threshold is meaningful, which is why it lives
|
|
634
|
+
* here and not on `HybridRetriever`.** The score here comes straight from the store's vector
|
|
635
|
+
* search: a calibrated similarity, comparable across queries and monotonic in how well the passage
|
|
636
|
+
* matches. A fused score is neither — see the score section on `HybridRetriever`. Threshold each
|
|
637
|
+
* leg, then fuse; never the other way round.
|
|
638
|
+
*
|
|
639
|
+
* Pick the number by measuring, not by taste: score questions you know the corpus answers against
|
|
640
|
+
* questions you know it does not, and put the floor in the gap between the two populations. It is
|
|
641
|
+
* specific to the embedding model and the distance metric, so it does not survive changing either.
|
|
642
|
+
* Left unset nothing is dropped, which is the previous behaviour exactly.
|
|
643
|
+
*/
|
|
644
|
+
minScore?: number;
|
|
645
|
+
}
|
|
598
646
|
/**
|
|
599
647
|
* Bridges an {@link EmbeddingProvider} + {@link VectorStore} into the core {@link Retriever} SPI:
|
|
600
648
|
* embed the query, then vector-search. This is what you wire as the agent's retriever (as a tool
|
|
@@ -603,7 +651,8 @@ declare class RedisVectorStore implements LexicalVectorStore {
|
|
|
603
651
|
declare class EmbeddingRetriever implements Retriever {
|
|
604
652
|
private readonly embedder;
|
|
605
653
|
private readonly store;
|
|
606
|
-
|
|
654
|
+
private readonly options;
|
|
655
|
+
constructor(embedder: EmbeddingProvider, store: VectorStore, options?: EmbeddingRetrieverOptions);
|
|
607
656
|
retrieve(query: string, options?: RetrieveOptions): Promise<Passage[]>;
|
|
608
657
|
}
|
|
609
658
|
|
|
@@ -724,6 +773,36 @@ interface HybridRetrieverOptions {
|
|
|
724
773
|
* vector search with lexical BM25. RRF scores each passage by `Σ weight / (k + rank)` across the
|
|
725
774
|
* lists it appears in, so it needs no score normalization between incompatible scales (cosine vs
|
|
726
775
|
* BM25). Deduplicates by passage id, so aligned chunk ids from the two retrievers reinforce.
|
|
776
|
+
*
|
|
777
|
+
* ## The score on a fused passage is not a similarity. Do not threshold it.
|
|
778
|
+
*
|
|
779
|
+
* Read this before using `passage.score` for anything other than the order it already produced.
|
|
780
|
+
*
|
|
781
|
+
* Not normalizing between scales is what makes RRF robust, and the price is that the number it
|
|
782
|
+
* emits is a function of **rank alone** — it never sees a similarity at all. Two consequences that
|
|
783
|
+
* bite in practice:
|
|
784
|
+
*
|
|
785
|
+
* - **It is not comparable to the score on a passage from {@link EmbeddingRetriever}**, even though
|
|
786
|
+
* it arrives in the same field, of the same type, from the same `Retriever` interface. There is no
|
|
787
|
+
* discriminator; a consumer holding a `Passage[]` cannot tell which kind of number it has. If you
|
|
788
|
+
* surface scores to a user or a model, know which retriever produced them.
|
|
789
|
+
* - **Its range is narrow, fixed, and says nothing about quality.** Every possible score lies in
|
|
790
|
+
* `[minWeight / (k + fetchTopK), Σ weights / (k + 1)]` — with the defaults (`k = 60`,
|
|
791
|
+
* `fetchTopK = 20`, two unweighted legs) that is `[0.0125, 0.0328]`. A perfect match and the
|
|
792
|
+
* least-bad member of a set of terrible matches both come back around `0.016`.
|
|
793
|
+
*
|
|
794
|
+
* The second point is why a relevance floor cannot live here. What the fused score actually measures
|
|
795
|
+
* is how strongly the legs *agree* — and agreement tracks nothing about correctness. When both legs
|
|
796
|
+
* return the same nearest nothing for an unanswerable query, they reinforce it to the ceiling, the
|
|
797
|
+
* same ceiling a unanimous correct answer gets. Measured on one corpus, the dense leg alone
|
|
798
|
+
* separated cleanly (weakest real answer 0.244, loudest absent-topic answer 0.143) while those same
|
|
799
|
+
* queries after fusion scored 0.032 and 0.033 — inverted. On another corpus the inversion may not
|
|
800
|
+
* appear; what does not change is that both numbers live in a band roughly one part in fifty wide,
|
|
801
|
+
* with no room for a threshold to mean anything.
|
|
802
|
+
*
|
|
803
|
+
* So: put the floor on the legs, with {@link EmbeddingRetrieverOptions.minScore}, and fuse what
|
|
804
|
+
* survives. `HybridRetriever` deliberately has no `minScore` of its own; a threshold option here
|
|
805
|
+
* would be an invitation to apply it to a number that cannot support one.
|
|
727
806
|
*/
|
|
728
807
|
declare class HybridRetriever implements Retriever {
|
|
729
808
|
private readonly retrievers;
|
|
@@ -789,4 +868,4 @@ interface RetrievalTool {
|
|
|
789
868
|
*/
|
|
790
869
|
declare function createRetrievalTool(retriever: Retriever, options?: RetrievalToolOptions): RetrievalTool;
|
|
791
870
|
|
|
792
|
-
export { type ChunkOptions, type ChunkRecord, EmbeddingRetriever, FilteredRetriever, HybridRetriever, type HybridRetrieverOptions, type IndexedDocument, type IngestChunksOptions, type IngestDocument, type IngestOptions, KeywordRetriever, type KeywordRetrieverOptions, LexicalRetriever, type LexicalVectorStore, MemoryVectorStore, type MetadataPatch, type PgClient, PgVectorStore, type PgVectorStoreOptions, type RedisSearchClient, RedisVectorSchemaMismatchError, RedisVectorStore, type RedisVectorStoreOptions, RerankingRetriever, type RerankingRetrieverOptions, type RetrievalTool, type RetrievalToolOptions, UnsafeRemovalError, type VectorRecord, type VectorSearchOptions, type VectorStore, applyMetadataPatch, chunkDocuments, chunkText, createRetrievalTool, documentIdOf, ingestChunks, ingestDocuments, isLexicalVectorStore };
|
|
871
|
+
export { type ChunkOptions, type ChunkRecord, EmbeddingRetriever, type EmbeddingRetrieverOptions, FilteredRetriever, HybridRetriever, type HybridRetrieverOptions, type IndexedDocument, type IngestChunksOptions, type IngestDocument, type IngestOptions, KeywordRetriever, type KeywordRetrieverOptions, LexicalRetriever, type LexicalVectorStore, MemoryVectorStore, type MetadataPatch, type PgClient, PgVectorStore, type PgVectorStoreOptions, type RedisSearchClient, RedisVectorSchemaMismatchError, RedisVectorStore, type RedisVectorStoreOptions, RerankingRetriever, type RerankingRetrieverOptions, type RetrievalTool, type RetrievalToolOptions, UnsafeRemovalError, type VectorRecord, type VectorSearchOptions, type VectorStore, applyMetadataPatch, chunkDocuments, chunkText, createRetrievalTool, documentIdOf, ingestChunks, ingestDocuments, isLexicalVectorStore };
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,9 @@ function chunkText(text, options = {}) {
|
|
|
9
9
|
if (normalized.length === 0) {
|
|
10
10
|
return [];
|
|
11
11
|
}
|
|
12
|
+
if (options.separator !== void 0 && options.separator.length > 0) {
|
|
13
|
+
return chunkByRecord(normalized, chunkSize, options.separator);
|
|
14
|
+
}
|
|
12
15
|
if (normalized.length <= chunkSize) {
|
|
13
16
|
return [
|
|
14
17
|
normalized
|
|
@@ -38,6 +41,40 @@ function chunkText(text, options = {}) {
|
|
|
38
41
|
return chunks;
|
|
39
42
|
}
|
|
40
43
|
__name(chunkText, "chunkText");
|
|
44
|
+
function chunkByRecord(text, chunkSize, separator) {
|
|
45
|
+
const chunks = [];
|
|
46
|
+
let current = "";
|
|
47
|
+
const flush = /* @__PURE__ */ __name(() => {
|
|
48
|
+
if (current.length > 0) {
|
|
49
|
+
chunks.push(current);
|
|
50
|
+
current = "";
|
|
51
|
+
}
|
|
52
|
+
}, "flush");
|
|
53
|
+
for (const record of text.split(separator)) {
|
|
54
|
+
const trimmed = record.trim();
|
|
55
|
+
if (trimmed.length === 0) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (trimmed.length > chunkSize) {
|
|
59
|
+
flush();
|
|
60
|
+
chunks.push(...chunkText(trimmed, {
|
|
61
|
+
chunkSize,
|
|
62
|
+
overlap: 0
|
|
63
|
+
}));
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const candidate = current.length > 0 ? `${current}${separator}${trimmed}` : trimmed;
|
|
67
|
+
if (candidate.length > chunkSize) {
|
|
68
|
+
flush();
|
|
69
|
+
current = trimmed;
|
|
70
|
+
} else {
|
|
71
|
+
current = candidate;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
flush();
|
|
75
|
+
return chunks;
|
|
76
|
+
}
|
|
77
|
+
__name(chunkByRecord, "chunkByRecord");
|
|
41
78
|
|
|
42
79
|
// src/vector-store.ts
|
|
43
80
|
function isLexicalVectorStore(store) {
|
|
@@ -1295,9 +1332,11 @@ var EmbeddingRetriever = class {
|
|
|
1295
1332
|
}
|
|
1296
1333
|
embedder;
|
|
1297
1334
|
store;
|
|
1298
|
-
|
|
1335
|
+
options;
|
|
1336
|
+
constructor(embedder, store, options = {}) {
|
|
1299
1337
|
this.embedder = embedder;
|
|
1300
1338
|
this.store = store;
|
|
1339
|
+
this.options = options;
|
|
1301
1340
|
}
|
|
1302
1341
|
async retrieve(query, options = {}) {
|
|
1303
1342
|
const [embedding] = await this.embedder.embed([
|
|
@@ -1306,12 +1345,14 @@ var EmbeddingRetriever = class {
|
|
|
1306
1345
|
if (embedding === void 0) {
|
|
1307
1346
|
return [];
|
|
1308
1347
|
}
|
|
1309
|
-
|
|
1348
|
+
const passages = await this.store.search(embedding, {
|
|
1310
1349
|
topK: options.topK ?? 5,
|
|
1311
1350
|
...options.filter !== void 0 ? {
|
|
1312
1351
|
filter: options.filter
|
|
1313
1352
|
} : {}
|
|
1314
1353
|
});
|
|
1354
|
+
const { minScore } = this.options;
|
|
1355
|
+
return minScore === void 0 ? passages : passages.filter((passage) => passage.score >= minScore);
|
|
1315
1356
|
}
|
|
1316
1357
|
};
|
|
1317
1358
|
|