@devilsdev/rag-connectors 3.6.0 → 3.7.1
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 +2 -2
- package/dist/index.cjs +643 -52
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @devilsdev/rag-connectors
|
|
2
2
|
|
|
3
|
-
Embedders, LLMs and vector stores for @devilsdev/rag-core: OpenAI, Anthropic, Cohere, Ollama, the AI SDK, Qdrant, pgvector and
|
|
3
|
+
Embedders, LLMs and vector stores for @devilsdev/rag-core: OpenAI, Anthropic, Cohere, Ollama, the AI SDK, Qdrant, pgvector, Pinecone and Weaviate.
|
|
4
4
|
|
|
5
5
|
Part of [@devilsdev/rag-pipeline-utils](https://www.npmjs.com/package/@devilsdev/rag-pipeline-utils), split into packages so you install only what you use. The objects are the same ones the full package exports; versions move in lockstep.
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Part of [@devilsdev/rag-pipeline-utils](https://www.npmjs.com/package/@devilsdev
|
|
|
8
8
|
npm install @devilsdev/rag-connectors
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Exports: `OpenAIConnector`, `AnthropicConnector`, `CohereConnector`, `OllamaConnector`, `LocalEmbedder`, `QdrantRetriever`, `PineconeRetriever`, `PgVectorRetriever`, `createAiSdkAdapter`.
|
|
11
|
+
Exports: `OpenAIConnector`, `AnthropicConnector`, `CohereConnector`, `OllamaConnector`, `LocalEmbedder`, `QdrantRetriever`, `PineconeRetriever`, `PgVectorRetriever`, `WeaviateRetriever`, `createAiSdkAdapter`.
|
|
12
12
|
|
|
13
13
|
Documentation: https://devilsdev.github.io/rag-pipeline-utils/
|
|
14
14
|
|
package/dist/index.cjs
CHANGED
|
@@ -233,8 +233,25 @@ var require_openai_connector = __commonJS({
|
|
|
233
233
|
_request(signal, fn) {
|
|
234
234
|
return this.withRetry(fn, this.config.maxRetries, this.config.retryDelay, {
|
|
235
235
|
signal
|
|
236
|
+
}).catch((err) => {
|
|
237
|
+
throw this._tagError(err);
|
|
236
238
|
});
|
|
237
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Name the provider on an HTTP error from the SDK, as the axios-based
|
|
242
|
+
* connectors do, so a caller such as the CLI can say which key was refused.
|
|
243
|
+
* A custom baseURL never reads OPENAI_API_KEY, so it gets no envVar.
|
|
244
|
+
* @private
|
|
245
|
+
*/
|
|
246
|
+
_tagError(err) {
|
|
247
|
+
if (err && typeof err === "object" && typeof err.status === "number") {
|
|
248
|
+
if (!err.provider) {
|
|
249
|
+
err.provider = this.config.baseURL ? "The OpenAI-compatible API" : "OpenAI";
|
|
250
|
+
}
|
|
251
|
+
if (!this.config.baseURL && !err.envVar) err.envVar = API_KEY_ENV;
|
|
252
|
+
}
|
|
253
|
+
return err;
|
|
254
|
+
}
|
|
238
255
|
/**
|
|
239
256
|
* Generate a chat completion using the provided query and optional RAG context.
|
|
240
257
|
*
|
|
@@ -278,19 +295,24 @@ var require_openai_connector = __commonJS({
|
|
|
278
295
|
this._requireApiKey();
|
|
279
296
|
const client = this._getClient();
|
|
280
297
|
const messages = this._buildMessages(query, context);
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
298
|
+
let stream;
|
|
299
|
+
try {
|
|
300
|
+
stream = await raceAbort(
|
|
301
|
+
client.chat.completions.create(
|
|
302
|
+
{
|
|
303
|
+
model: options.model || this.config.model,
|
|
304
|
+
messages,
|
|
305
|
+
temperature: options.temperature ?? this.config.temperature,
|
|
306
|
+
max_tokens: options.maxTokens ?? this.config.maxTokens,
|
|
307
|
+
stream: true
|
|
308
|
+
},
|
|
309
|
+
signal ? { signal } : void 0
|
|
310
|
+
),
|
|
311
|
+
signal
|
|
312
|
+
);
|
|
313
|
+
} catch (err) {
|
|
314
|
+
throw this._tagError(err);
|
|
315
|
+
}
|
|
294
316
|
for await (const chunk of stream) {
|
|
295
317
|
throwIfAborted(signal);
|
|
296
318
|
const token = chunk.choices[0]?.delta?.content || "";
|
|
@@ -1350,7 +1372,7 @@ var require_qdrant_retriever = __commonJS({
|
|
|
1350
1372
|
if (typeof fetchImpl !== "function") {
|
|
1351
1373
|
throw new Error("QdrantRetriever needs fetch (Node.js 18 or later)");
|
|
1352
1374
|
}
|
|
1353
|
-
this.url = String(url).replace(
|
|
1375
|
+
this.url = String(url).replace(/(?<!\/)\/+$/, "");
|
|
1354
1376
|
this.collection = collection;
|
|
1355
1377
|
this.dimensions = dimensions;
|
|
1356
1378
|
this.createCollection = createCollection;
|
|
@@ -1621,12 +1643,15 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1621
1643
|
var CONTROL_PLANE = "https://api.pinecone.io";
|
|
1622
1644
|
var DEFAULT_NAMESPACE = "rag_documents";
|
|
1623
1645
|
var UPSERT_BATCH = 100;
|
|
1646
|
+
var EMBED_UPSERT_BATCH = 96;
|
|
1647
|
+
var REQUEST_BYTES = 2e6 - 1024;
|
|
1624
1648
|
var DELETE_BATCH = 1e3;
|
|
1625
1649
|
var PAGE = 100;
|
|
1626
1650
|
var SEARCH_MAX = 1e4;
|
|
1627
1651
|
var PLAIN_ID = /^[\x20-\x7e]{1,512}$/;
|
|
1628
1652
|
var FIELDS = ["rag_id", "rag_content", "rag_metadata"];
|
|
1629
1653
|
var OURS = { rag_id: { $exists: true } };
|
|
1654
|
+
var PROBE_QUERY = "document";
|
|
1630
1655
|
function recordId(id) {
|
|
1631
1656
|
const s = String(id);
|
|
1632
1657
|
if (PLAIN_ID.test(s)) return s;
|
|
@@ -1671,6 +1696,7 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1671
1696
|
* @param {string} [options.namespace='rag_documents'] - A namespace this retriever owns
|
|
1672
1697
|
* @param {'auto'|'documents'|'vectors'} [options.api='auto'] - Document index (Documents API) or vector index; auto reads it from the index description
|
|
1673
1698
|
* @param {string} [options.vectorField] - The document index's dense_vector field (default: the schema's)
|
|
1699
|
+
* @param {string} [options.textField] - The document index's text field that Pinecone embeds (integrated embedding; default: the schema's, when the index has no dense_vector field)
|
|
1674
1700
|
* @param {number} [options.freshnessTimeoutMs=30000] - How long a read waits for this retriever's writes to be indexed; 0 to not wait
|
|
1675
1701
|
* @param {string} [options.cacheKey] - Cache identity; change it when the index changes
|
|
1676
1702
|
* @param {Function} [options.fetch] - fetch implementation (defaults to the global)
|
|
@@ -1683,6 +1709,7 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1683
1709
|
namespace = DEFAULT_NAMESPACE,
|
|
1684
1710
|
api = "auto",
|
|
1685
1711
|
vectorField,
|
|
1712
|
+
textField,
|
|
1686
1713
|
freshnessTimeoutMs = 3e4,
|
|
1687
1714
|
cacheKey,
|
|
1688
1715
|
fetch: fetchImpl = globalThis.fetch
|
|
@@ -1705,6 +1732,16 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1705
1732
|
'PineconeRetriever: api must be "auto", "documents" or "vectors"'
|
|
1706
1733
|
);
|
|
1707
1734
|
}
|
|
1735
|
+
if (vectorField && textField) {
|
|
1736
|
+
throw new Error(
|
|
1737
|
+
"PineconeRetriever: pass vectorField (your vectors) or textField (Pinecone embeds the text), not both"
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1740
|
+
if (textField && api === "vectors") {
|
|
1741
|
+
throw new Error(
|
|
1742
|
+
'PineconeRetriever: textField needs a document index (api "documents" or "auto")'
|
|
1743
|
+
);
|
|
1744
|
+
}
|
|
1708
1745
|
if (typeof fetchImpl !== "function") {
|
|
1709
1746
|
throw new Error("PineconeRetriever needs fetch (Node.js 18 or later)");
|
|
1710
1747
|
}
|
|
@@ -1716,6 +1753,7 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1716
1753
|
this._host = host ? normalizeHost(host) : null;
|
|
1717
1754
|
this._api = api;
|
|
1718
1755
|
this._vectorField = vectorField;
|
|
1756
|
+
this._textField = textField;
|
|
1719
1757
|
this._fetch = fetchImpl === globalThis.fetch ? fetchImpl.bind(globalThis) : fetchImpl;
|
|
1720
1758
|
this._resolving = null;
|
|
1721
1759
|
this._writeLsn = 0;
|
|
@@ -1731,6 +1769,18 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1731
1769
|
if (role !== "retriever" || !this.cacheKey) return null;
|
|
1732
1770
|
return `pinecone|${this._host || this.index}|${this.namespace}|${this.cacheKey}`;
|
|
1733
1771
|
}
|
|
1772
|
+
/**
|
|
1773
|
+
* Whether the index embeds text itself (integrated embedding): then
|
|
1774
|
+
* store() needs no vectors and retrieve() takes the query text, so no
|
|
1775
|
+
* embedder is needed. The CLI checks this before requiring an embedder.
|
|
1776
|
+
* Reads the index description on first use.
|
|
1777
|
+
* @param {{ signal?: AbortSignal }} [options]
|
|
1778
|
+
* @returns {Promise<boolean>}
|
|
1779
|
+
*/
|
|
1780
|
+
async embedsText({ signal } = {}) {
|
|
1781
|
+
await this._resolve(signal);
|
|
1782
|
+
return this._integrated;
|
|
1783
|
+
}
|
|
1734
1784
|
/**
|
|
1735
1785
|
* One REST call; resolves the parsed body and the response headers.
|
|
1736
1786
|
* @private
|
|
@@ -1779,12 +1829,21 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1779
1829
|
}
|
|
1780
1830
|
return { json, headers: res.headers };
|
|
1781
1831
|
}
|
|
1832
|
+
/** True on a document index that embeds text itself. @private */
|
|
1833
|
+
get _integrated() {
|
|
1834
|
+
return this._api === "documents" && Boolean(this._textField);
|
|
1835
|
+
}
|
|
1782
1836
|
/**
|
|
1783
1837
|
* The index's host and kind, from its description (once): a document
|
|
1784
|
-
* index's dense_vector field and dimension
|
|
1838
|
+
* index's dense_vector field and dimension or the text field it embeds,
|
|
1839
|
+
* or a vector index's dimension.
|
|
1785
1840
|
* @private
|
|
1786
1841
|
*/
|
|
1787
1842
|
_resolve(signal) {
|
|
1843
|
+
if (this._host && this._textField) {
|
|
1844
|
+
this._api = "documents";
|
|
1845
|
+
return Promise.resolve();
|
|
1846
|
+
}
|
|
1788
1847
|
if (this._host && this._api !== "auto" && (this._api === "vectors" || this._vectorField)) {
|
|
1789
1848
|
return Promise.resolve();
|
|
1790
1849
|
}
|
|
@@ -1830,15 +1889,27 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1830
1889
|
const fields = info.schema && info.schema.fields || null;
|
|
1831
1890
|
const named = fields ? Object.entries(fields).filter(([name]) => !name.startsWith("_")) : [];
|
|
1832
1891
|
const denseFields = named.filter(([, f]) => f && f.type === "dense_vector");
|
|
1892
|
+
const textFields = named.filter(
|
|
1893
|
+
([, f]) => f && f.type === "string" && f.embed
|
|
1894
|
+
);
|
|
1833
1895
|
if (this._api === "auto") {
|
|
1834
1896
|
this._api = named.length > 0 ? "documents" : "vectors";
|
|
1835
1897
|
}
|
|
1836
|
-
if (this.
|
|
1898
|
+
if (this._textField) {
|
|
1899
|
+
if (this._api !== "documents" || !textFields.some(([name]) => name === this._textField)) {
|
|
1900
|
+
throw new Error(
|
|
1901
|
+
`PineconeRetriever: index "${info.name}" has no text field named "${this._textField}" that it embeds`
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
1904
|
+
} else if (this._api === "documents") {
|
|
1837
1905
|
const field = this._vectorField ? denseFields.find(([name]) => name === this._vectorField) : denseFields[0];
|
|
1906
|
+
if (!field && !this._vectorField && textFields.length > 0) {
|
|
1907
|
+
this._textField = textFields[0][0];
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1838
1910
|
if (!field) {
|
|
1839
|
-
const embeds = fields && Object.values(fields).some((f) => f && f.embed);
|
|
1840
1911
|
throw new Error(
|
|
1841
|
-
|
|
1912
|
+
`PineconeRetriever: index "${info.name}" has no dense_vector field${this._vectorField ? ` named "${this._vectorField}"` : ""}`
|
|
1842
1913
|
);
|
|
1843
1914
|
}
|
|
1844
1915
|
this._vectorField = field[0];
|
|
@@ -1871,20 +1942,21 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1871
1942
|
/**
|
|
1872
1943
|
* A search, repeated until it reflects this retriever's writes (when the
|
|
1873
1944
|
* index reports log sequence numbers) or `freshnessTimeoutMs` passes.
|
|
1874
|
-
*
|
|
1945
|
+
* `query` is a vector, or the query text on an index that embeds text
|
|
1946
|
+
* itself. Resolves `[{ id, score, fields }]`.
|
|
1875
1947
|
* @private
|
|
1876
1948
|
*/
|
|
1877
|
-
async _search(
|
|
1949
|
+
async _search(query, topK, signal, filter) {
|
|
1878
1950
|
await this._resolve(signal);
|
|
1879
1951
|
const documents = this._api === "documents";
|
|
1880
1952
|
const [path, body] = documents ? [
|
|
1881
1953
|
this._docs("search"),
|
|
1882
1954
|
{
|
|
1883
1955
|
score_by: [
|
|
1884
|
-
{
|
|
1956
|
+
this._integrated ? { type: "embed", fields: [this._textField], query } : {
|
|
1885
1957
|
type: "dense_vector",
|
|
1886
1958
|
fields: [this._vectorField],
|
|
1887
|
-
values:
|
|
1959
|
+
values: query
|
|
1888
1960
|
}
|
|
1889
1961
|
],
|
|
1890
1962
|
top_k: topK,
|
|
@@ -1894,7 +1966,7 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1894
1966
|
] : [
|
|
1895
1967
|
"/query",
|
|
1896
1968
|
{
|
|
1897
|
-
vector,
|
|
1969
|
+
vector: query,
|
|
1898
1970
|
topK,
|
|
1899
1971
|
namespace: this.namespace,
|
|
1900
1972
|
includeMetadata: true,
|
|
@@ -1925,17 +1997,25 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1925
1997
|
/** Waits until this retriever's writes are indexed (for list and size). @private */
|
|
1926
1998
|
async _waitForWrites(signal) {
|
|
1927
1999
|
if (!this._writeLsn) return;
|
|
2000
|
+
const probe = this._probe();
|
|
2001
|
+
if (probe) await this._search(probe, 1, signal);
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* What list() and size() search with: a unit vector of the index's
|
|
2005
|
+
* dimension, or a fixed text on an index that embeds text itself.
|
|
2006
|
+
* @private
|
|
2007
|
+
*/
|
|
2008
|
+
_probe() {
|
|
2009
|
+
if (this._integrated) return PROBE_QUERY;
|
|
1928
2010
|
const dimension = this._indexDimension || this._dimension;
|
|
1929
|
-
if (!dimension) return;
|
|
1930
|
-
|
|
1931
|
-
new Array(dimension).fill(0).map((_, i) => i === 0 ? 1 : 0),
|
|
1932
|
-
1,
|
|
1933
|
-
signal
|
|
1934
|
-
);
|
|
2011
|
+
if (!dimension) return null;
|
|
2012
|
+
return new Array(dimension).fill(0).map((_, i) => i === 0 ? 1 : 0);
|
|
1935
2013
|
}
|
|
1936
2014
|
/**
|
|
1937
|
-
* Inserts or replaces documents.
|
|
1938
|
-
*
|
|
2015
|
+
* Inserts or replaces documents. On an index that embeds text itself,
|
|
2016
|
+
* Pinecone embeds `content` (required there) and `vector` is ignored.
|
|
2017
|
+
* @param {Array<{ id: string, vector?: number[], content?: string, metadata?: Object }>} docs
|
|
2018
|
+
* `vector` is required unless the index embeds text itself
|
|
1939
2019
|
* @returns {Promise<void>}
|
|
1940
2020
|
*/
|
|
1941
2021
|
async store(docs) {
|
|
@@ -1944,6 +2024,7 @@ var require_pinecone_retriever = __commonJS({
|
|
|
1944
2024
|
}
|
|
1945
2025
|
if (docs.length === 0) return;
|
|
1946
2026
|
await this._resolve();
|
|
2027
|
+
if (this._integrated) return this._storeText(docs);
|
|
1947
2028
|
const expected = this._indexDimension || this._dimension;
|
|
1948
2029
|
let dimensions = null;
|
|
1949
2030
|
const records = docs.map((doc) => {
|
|
@@ -2012,29 +2093,94 @@ var require_pinecone_retriever = __commonJS({
|
|
|
2012
2093
|
}
|
|
2013
2094
|
this._dimension = dimensions;
|
|
2014
2095
|
}
|
|
2096
|
+
/** store() on an index that embeds text itself. @private */
|
|
2097
|
+
async _storeText(docs) {
|
|
2098
|
+
const records = docs.map((doc) => {
|
|
2099
|
+
const id = doc && doc.id;
|
|
2100
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
2101
|
+
throw new Error(
|
|
2102
|
+
"PineconeRetriever.store(): every document needs a string id"
|
|
2103
|
+
);
|
|
2104
|
+
}
|
|
2105
|
+
if (typeof doc.content !== "string" || doc.content.trim() === "") {
|
|
2106
|
+
throw new Error(
|
|
2107
|
+
`PineconeRetriever.store(): document "${id}" has no content for Pinecone to embed (the index embeds ${this._textField})`
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
2110
|
+
const metadata = doc.metadata || {};
|
|
2111
|
+
return {
|
|
2112
|
+
_id: recordId(id),
|
|
2113
|
+
[this._textField]: doc.content,
|
|
2114
|
+
rag_id: id,
|
|
2115
|
+
rag_content: doc.content,
|
|
2116
|
+
rag_metadata: JSON.stringify(metadata),
|
|
2117
|
+
...metadata.source !== void 0 && metadata.source !== null && {
|
|
2118
|
+
rag_source: String(metadata.source)
|
|
2119
|
+
}
|
|
2120
|
+
};
|
|
2121
|
+
});
|
|
2122
|
+
const encoder = new TextEncoder();
|
|
2123
|
+
let batch = [];
|
|
2124
|
+
let bytes = 0;
|
|
2125
|
+
const send = async () => {
|
|
2126
|
+
if (batch.length === 0) return;
|
|
2127
|
+
await this._data(
|
|
2128
|
+
"POST",
|
|
2129
|
+
this._docs("upsert"),
|
|
2130
|
+
{ documents: batch },
|
|
2131
|
+
{ write: true }
|
|
2132
|
+
);
|
|
2133
|
+
batch = [];
|
|
2134
|
+
bytes = 0;
|
|
2135
|
+
};
|
|
2136
|
+
for (const record of records) {
|
|
2137
|
+
const size = encoder.encode(JSON.stringify(record)).length + 1;
|
|
2138
|
+
if (batch.length === EMBED_UPSERT_BATCH || batch.length > 0 && bytes + size > REQUEST_BYTES) {
|
|
2139
|
+
await send();
|
|
2140
|
+
}
|
|
2141
|
+
batch.push(record);
|
|
2142
|
+
bytes += size;
|
|
2143
|
+
}
|
|
2144
|
+
await send();
|
|
2145
|
+
}
|
|
2015
2146
|
/**
|
|
2016
|
-
* The documents most similar to a query vector
|
|
2017
|
-
*
|
|
2018
|
-
*
|
|
2019
|
-
*
|
|
2020
|
-
*
|
|
2147
|
+
* The documents most similar to a query vector, or to the query text on
|
|
2148
|
+
* an index that embeds text itself (Pinecone embeds the query; a
|
|
2149
|
+
* `queryVector` is then ignored).
|
|
2150
|
+
* @param {number[]|string|{ query?: string, queryVector?: number[], topK?: number, signal?: AbortSignal }} request
|
|
2151
|
+
* A vector, the query text, or an object with them: `queryVector`, or
|
|
2152
|
+
* `query` on an index that embeds text itself (required; optional in
|
|
2153
|
+
* the type to match the retriever contract)
|
|
2154
|
+
* @param {number} [k=10] - Results when `request` is a vector or text
|
|
2021
2155
|
* @returns {Promise<Array<{ id: string, score: number, content: string, metadata: Object }>>}
|
|
2022
2156
|
*/
|
|
2023
2157
|
async retrieve(request, k = 10) {
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2158
|
+
let input;
|
|
2159
|
+
if (Array.isArray(request)) input = { queryVector: request };
|
|
2160
|
+
else if (typeof request === "string") input = { query: request };
|
|
2161
|
+
else input = request || {};
|
|
2162
|
+
const { query, queryVector, topK = k, signal } = input;
|
|
2029
2163
|
if (!Number.isInteger(topK) || topK < 1) {
|
|
2030
2164
|
throw new Error(
|
|
2031
2165
|
"PineconeRetriever.retrieve(): topK must be a positive integer"
|
|
2032
2166
|
);
|
|
2033
2167
|
}
|
|
2034
|
-
checkVector(queryVector, "query");
|
|
2035
2168
|
throwIfAborted(signal);
|
|
2169
|
+
await this._resolve(signal);
|
|
2170
|
+
if (!this._integrated) {
|
|
2171
|
+
if (queryVector === void 0 || queryVector === null) {
|
|
2172
|
+
throw new Error(
|
|
2173
|
+
"PineconeRetriever.retrieve(): needs a queryVector; this index does not embed text itself, so use an embedder"
|
|
2174
|
+
);
|
|
2175
|
+
}
|
|
2176
|
+
checkVector(queryVector, "query");
|
|
2177
|
+
} else if (typeof query !== "string" || query.trim() === "") {
|
|
2178
|
+
throw new Error(
|
|
2179
|
+
`PineconeRetriever.retrieve(): the index embeds text itself (integrated embedding, field ${this._textField}), so it needs the query text: pass { query }`
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2036
2182
|
const matches = await this._search(
|
|
2037
|
-
queryVector,
|
|
2183
|
+
this._integrated ? query : queryVector,
|
|
2038
2184
|
Math.min(topK, 1e4),
|
|
2039
2185
|
signal
|
|
2040
2186
|
);
|
|
@@ -2113,15 +2259,10 @@ var require_pinecone_retriever = __commonJS({
|
|
|
2113
2259
|
}
|
|
2114
2260
|
/** Documents API: fetch by filter, page by page. @private */
|
|
2115
2261
|
async _listDocuments(limit) {
|
|
2116
|
-
const
|
|
2117
|
-
if (
|
|
2262
|
+
const probe = this._probe();
|
|
2263
|
+
if (probe) {
|
|
2118
2264
|
const max = Math.min(limit || SEARCH_MAX, SEARCH_MAX);
|
|
2119
|
-
const found = await this._search(
|
|
2120
|
-
new Array(dimension).fill(0).map((_, i) => i === 0 ? 1 : 0),
|
|
2121
|
-
max,
|
|
2122
|
-
void 0,
|
|
2123
|
-
OURS
|
|
2124
|
-
);
|
|
2265
|
+
const found = await this._search(probe, max, void 0, OURS);
|
|
2125
2266
|
if (found.length < SEARCH_MAX || limit) {
|
|
2126
2267
|
const seen = /* @__PURE__ */ new Set();
|
|
2127
2268
|
return found.filter((m) => !seen.has(m.id) && seen.add(m.id)).map((m) => fromFields(m.fields, m.id));
|
|
@@ -2540,6 +2681,453 @@ var require_pgvector_retriever = __commonJS({
|
|
|
2540
2681
|
}
|
|
2541
2682
|
});
|
|
2542
2683
|
|
|
2684
|
+
// src/retrieval/weaviate-retriever.js
|
|
2685
|
+
var require_weaviate_retriever = __commonJS({
|
|
2686
|
+
"src/retrieval/weaviate-retriever.js"(exports2, module2) {
|
|
2687
|
+
"use strict";
|
|
2688
|
+
var { throwIfAborted } = require_connector_utils();
|
|
2689
|
+
var { pointId } = require_qdrant_retriever();
|
|
2690
|
+
var COLLECTION = /^[A-Za-z][A-Za-z0-9_]{0,254}$/;
|
|
2691
|
+
var DEFAULT_COLLECTION = "RagDocuments";
|
|
2692
|
+
var PAGE = 256;
|
|
2693
|
+
var UPSERT_BATCH = 256;
|
|
2694
|
+
var DELETE_BATCH = 256;
|
|
2695
|
+
var PROPERTIES = [
|
|
2696
|
+
{
|
|
2697
|
+
name: "rag_id",
|
|
2698
|
+
dataType: ["text"],
|
|
2699
|
+
tokenization: "field",
|
|
2700
|
+
indexFilterable: true,
|
|
2701
|
+
indexSearchable: false
|
|
2702
|
+
},
|
|
2703
|
+
{ name: "content", dataType: ["text"], indexFilterable: false },
|
|
2704
|
+
{
|
|
2705
|
+
name: "rag_source",
|
|
2706
|
+
dataType: ["text"],
|
|
2707
|
+
tokenization: "field",
|
|
2708
|
+
indexFilterable: true,
|
|
2709
|
+
indexSearchable: false
|
|
2710
|
+
},
|
|
2711
|
+
{
|
|
2712
|
+
name: "rag_metadata",
|
|
2713
|
+
dataType: ["text"],
|
|
2714
|
+
indexFilterable: false,
|
|
2715
|
+
indexSearchable: false
|
|
2716
|
+
}
|
|
2717
|
+
];
|
|
2718
|
+
var OURS = { path: ["rag_id"], operator: "Like", valueText: "*" };
|
|
2719
|
+
var OURS_GRAPHQL = '{ path: ["rag_id"], operator: Like, valueText: "*" }';
|
|
2720
|
+
function checkVector(vector, id) {
|
|
2721
|
+
if (!Array.isArray(vector) || vector.length === 0) {
|
|
2722
|
+
throw new Error(`WeaviateRetriever: document "${id}" has no vector`);
|
|
2723
|
+
}
|
|
2724
|
+
if (!vector.every((x) => typeof x === "number" && Number.isFinite(x))) {
|
|
2725
|
+
throw new Error(
|
|
2726
|
+
`WeaviateRetriever: document "${id}" has a vector with a non-finite value`
|
|
2727
|
+
);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
function trimSlashes(url) {
|
|
2731
|
+
let end = url.length;
|
|
2732
|
+
while (end > 0 && url[end - 1] === "/") end--;
|
|
2733
|
+
return url.slice(0, end);
|
|
2734
|
+
}
|
|
2735
|
+
function parseMetadata(text) {
|
|
2736
|
+
if (!text) return {};
|
|
2737
|
+
try {
|
|
2738
|
+
const value = JSON.parse(text);
|
|
2739
|
+
return value && typeof value === "object" ? value : {};
|
|
2740
|
+
} catch {
|
|
2741
|
+
return {};
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
function errorDetail(json) {
|
|
2745
|
+
const list = json && (json.error || json.errors) || [];
|
|
2746
|
+
if (Array.isArray(list) && list.length) {
|
|
2747
|
+
return list.map((e) => e && e.message || String(e)).join("; ");
|
|
2748
|
+
}
|
|
2749
|
+
return json && json.message || "";
|
|
2750
|
+
}
|
|
2751
|
+
var WeaviateRetriever = class {
|
|
2752
|
+
/**
|
|
2753
|
+
* @param {Object} [options]
|
|
2754
|
+
* @param {string} [options.url='http://localhost:8080'] - Weaviate's REST endpoint (without /v1)
|
|
2755
|
+
* @param {string} [options.apiKey] - Sent as `Authorization: Bearer <key>`
|
|
2756
|
+
* @param {string} [options.collection='RagDocuments'] - Class name; the first letter is upper-cased, as Weaviate does
|
|
2757
|
+
* @param {number} [options.dimensions] - Expected vector size; defaults to the collection's or the first stored vector's
|
|
2758
|
+
* @param {boolean} [options.createCollection=true] - Create the collection (and any missing properties) on first store
|
|
2759
|
+
* @param {string} [options.cacheKey] - Cache identity; change it when the collection changes
|
|
2760
|
+
* @param {Function} [options.fetch] - fetch implementation (defaults to the global)
|
|
2761
|
+
*/
|
|
2762
|
+
constructor(options = {}) {
|
|
2763
|
+
const {
|
|
2764
|
+
url = "http://localhost:8080",
|
|
2765
|
+
apiKey,
|
|
2766
|
+
collection = DEFAULT_COLLECTION,
|
|
2767
|
+
dimensions,
|
|
2768
|
+
createCollection = true,
|
|
2769
|
+
cacheKey,
|
|
2770
|
+
fetch: fetchImpl = globalThis.fetch
|
|
2771
|
+
} = options;
|
|
2772
|
+
if (typeof collection !== "string" || !COLLECTION.test(collection)) {
|
|
2773
|
+
throw new Error(
|
|
2774
|
+
`WeaviateRetriever: invalid collection name "${collection}" (a letter, then letters, digits and "_")`
|
|
2775
|
+
);
|
|
2776
|
+
}
|
|
2777
|
+
if (dimensions !== void 0 && !(Number.isInteger(dimensions) && dimensions > 0)) {
|
|
2778
|
+
throw new Error(
|
|
2779
|
+
"WeaviateRetriever: `dimensions` must be a positive integer"
|
|
2780
|
+
);
|
|
2781
|
+
}
|
|
2782
|
+
if (typeof fetchImpl !== "function") {
|
|
2783
|
+
throw new Error("WeaviateRetriever needs fetch (Node.js 18 or later)");
|
|
2784
|
+
}
|
|
2785
|
+
this.url = trimSlashes(String(url));
|
|
2786
|
+
this.collection = collection[0].toUpperCase() + collection.slice(1);
|
|
2787
|
+
this.dimensions = dimensions;
|
|
2788
|
+
this.createCollection = createCollection;
|
|
2789
|
+
this.cacheKey = cacheKey;
|
|
2790
|
+
this._apiKey = apiKey;
|
|
2791
|
+
this._fetch = fetchImpl === globalThis.fetch ? fetchImpl.bind(globalThis) : fetchImpl;
|
|
2792
|
+
this._ready = null;
|
|
2793
|
+
this._exists = false;
|
|
2794
|
+
}
|
|
2795
|
+
/**
|
|
2796
|
+
* Cache identity for createRagPipeline({ cache }): none unless you set
|
|
2797
|
+
* `cacheKey`, since other clients can change the collection.
|
|
2798
|
+
* @param {'retriever'} role
|
|
2799
|
+
* @returns {string|null}
|
|
2800
|
+
*/
|
|
2801
|
+
getCacheKey(role) {
|
|
2802
|
+
if (role !== "retriever" || !this.cacheKey) return null;
|
|
2803
|
+
return `weaviate|${this.url}|${this.collection}|${this.cacheKey}`;
|
|
2804
|
+
}
|
|
2805
|
+
/** The message without the API key, should a server ever echo it. */
|
|
2806
|
+
_redact(message) {
|
|
2807
|
+
const text = String(message);
|
|
2808
|
+
return this._apiKey ? text.split(this._apiKey).join("***") : text;
|
|
2809
|
+
}
|
|
2810
|
+
/**
|
|
2811
|
+
* One REST call; resolves the parsed body.
|
|
2812
|
+
* @private
|
|
2813
|
+
* @param {string} method
|
|
2814
|
+
* @param {string} path - After /v1
|
|
2815
|
+
* @param {Object} [body]
|
|
2816
|
+
* @param {{ signal?: AbortSignal, allow404?: boolean }} [options] - allow404 resolves null on 404
|
|
2817
|
+
* @returns {Promise<any>}
|
|
2818
|
+
*/
|
|
2819
|
+
async _request(method, path, body, { signal, allow404 = false } = {}) {
|
|
2820
|
+
throwIfAborted(signal);
|
|
2821
|
+
const headers = { "content-type": "application/json" };
|
|
2822
|
+
if (this._apiKey) headers.authorization = `Bearer ${this._apiKey}`;
|
|
2823
|
+
let res;
|
|
2824
|
+
try {
|
|
2825
|
+
res = await this._fetch(`${this.url}/v1${path}`, {
|
|
2826
|
+
method,
|
|
2827
|
+
headers,
|
|
2828
|
+
...body !== void 0 && { body: JSON.stringify(body) },
|
|
2829
|
+
...signal && { signal }
|
|
2830
|
+
});
|
|
2831
|
+
} catch (err) {
|
|
2832
|
+
throwIfAborted(signal);
|
|
2833
|
+
throw new Error(
|
|
2834
|
+
this._redact(
|
|
2835
|
+
`WeaviateRetriever: cannot reach Weaviate at ${this.url} (${err.message})`
|
|
2836
|
+
)
|
|
2837
|
+
);
|
|
2838
|
+
}
|
|
2839
|
+
if (res.status === 404 && allow404) return null;
|
|
2840
|
+
const text = await res.text();
|
|
2841
|
+
let json;
|
|
2842
|
+
try {
|
|
2843
|
+
json = text ? JSON.parse(text) : {};
|
|
2844
|
+
} catch {
|
|
2845
|
+
json = {};
|
|
2846
|
+
}
|
|
2847
|
+
if (!res.ok) {
|
|
2848
|
+
if (res.status === 401 || res.status === 403) {
|
|
2849
|
+
throw new Error(
|
|
2850
|
+
`WeaviateRetriever: Weaviate refused the request (${res.status}); check WEAVIATE_API_KEY`
|
|
2851
|
+
);
|
|
2852
|
+
}
|
|
2853
|
+
const detail = errorDetail(json) || text || res.statusText;
|
|
2854
|
+
const where = path.split("?")[0];
|
|
2855
|
+
throw new Error(
|
|
2856
|
+
this._redact(
|
|
2857
|
+
`WeaviateRetriever: ${method} ${where} failed (${res.status}): ${detail}`
|
|
2858
|
+
)
|
|
2859
|
+
);
|
|
2860
|
+
}
|
|
2861
|
+
return json;
|
|
2862
|
+
}
|
|
2863
|
+
/** A GraphQL query; resolves `data`, throws on GraphQL errors. */
|
|
2864
|
+
async _graphql(query, signal) {
|
|
2865
|
+
const json = await this._request("POST", "/graphql", { query }, { signal });
|
|
2866
|
+
if (json.errors && json.errors.length) {
|
|
2867
|
+
throw new Error(
|
|
2868
|
+
this._redact(
|
|
2869
|
+
`WeaviateRetriever: GraphQL query failed: ${errorDetail(json)}`
|
|
2870
|
+
)
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2873
|
+
return json.data || {};
|
|
2874
|
+
}
|
|
2875
|
+
/** The collection's schema, or null when it doesn't exist. */
|
|
2876
|
+
_schema(signal) {
|
|
2877
|
+
return this._request("GET", `/schema/${this.collection}`, void 0, {
|
|
2878
|
+
signal,
|
|
2879
|
+
allow404: true
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2882
|
+
/** Whether the collection exists; only a yes is remembered. */
|
|
2883
|
+
async _collectionExists(signal) {
|
|
2884
|
+
if (!this._exists) this._exists = Boolean(await this._schema(signal));
|
|
2885
|
+
return this._exists;
|
|
2886
|
+
}
|
|
2887
|
+
_ensureCollection() {
|
|
2888
|
+
if (!this._ready) {
|
|
2889
|
+
this._ready = (async () => {
|
|
2890
|
+
if (this.createCollection) {
|
|
2891
|
+
const schema = await this._schema();
|
|
2892
|
+
if (!schema) {
|
|
2893
|
+
try {
|
|
2894
|
+
await this._request("POST", "/schema", {
|
|
2895
|
+
class: this.collection,
|
|
2896
|
+
vectorizer: "none",
|
|
2897
|
+
vectorIndexType: "hnsw",
|
|
2898
|
+
vectorIndexConfig: { distance: "cosine" },
|
|
2899
|
+
properties: PROPERTIES
|
|
2900
|
+
});
|
|
2901
|
+
} catch (err) {
|
|
2902
|
+
if (!/already exists/i.test(err.message)) throw err;
|
|
2903
|
+
}
|
|
2904
|
+
} else {
|
|
2905
|
+
const have = new Set((schema.properties || []).map((p) => p.name));
|
|
2906
|
+
for (const property of PROPERTIES) {
|
|
2907
|
+
if (!have.has(property.name)) {
|
|
2908
|
+
await this._request(
|
|
2909
|
+
"POST",
|
|
2910
|
+
`/schema/${this.collection}/properties`,
|
|
2911
|
+
property
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
this._exists = true;
|
|
2917
|
+
}
|
|
2918
|
+
if (!this.dimensions) {
|
|
2919
|
+
const page = await this._request(
|
|
2920
|
+
"GET",
|
|
2921
|
+
`/objects?class=${this.collection}&limit=1&include=vector`,
|
|
2922
|
+
void 0,
|
|
2923
|
+
{ allow404: true }
|
|
2924
|
+
);
|
|
2925
|
+
const vector = page && page.objects && page.objects[0] && page.objects[0].vector;
|
|
2926
|
+
if (Array.isArray(vector) && vector.length) {
|
|
2927
|
+
this.dimensions = vector.length;
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
})().catch((err) => {
|
|
2931
|
+
this._ready = null;
|
|
2932
|
+
throw err;
|
|
2933
|
+
});
|
|
2934
|
+
}
|
|
2935
|
+
return this._ready;
|
|
2936
|
+
}
|
|
2937
|
+
/**
|
|
2938
|
+
* Inserts or replaces documents.
|
|
2939
|
+
* @param {Array<{ id: string, vector: number[], content?: string, metadata?: Object }>} docs
|
|
2940
|
+
* @returns {Promise<void>}
|
|
2941
|
+
*/
|
|
2942
|
+
async store(docs) {
|
|
2943
|
+
if (!Array.isArray(docs))
|
|
2944
|
+
throw new Error("WeaviateRetriever.store() takes an array");
|
|
2945
|
+
if (docs.length === 0) return;
|
|
2946
|
+
let dimensions = null;
|
|
2947
|
+
const objects = docs.map((doc) => {
|
|
2948
|
+
const id = doc && doc.id;
|
|
2949
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
2950
|
+
throw new Error(
|
|
2951
|
+
"WeaviateRetriever.store(): every document needs a string id"
|
|
2952
|
+
);
|
|
2953
|
+
}
|
|
2954
|
+
checkVector(doc.vector, id);
|
|
2955
|
+
if (dimensions === null) dimensions = doc.vector.length;
|
|
2956
|
+
else if (doc.vector.length !== dimensions) {
|
|
2957
|
+
throw new Error(
|
|
2958
|
+
`WeaviateRetriever.store(): document "${id}" has ${doc.vector.length} dimensions, the batch has ${dimensions}`
|
|
2959
|
+
);
|
|
2960
|
+
}
|
|
2961
|
+
const metadata = doc.metadata || {};
|
|
2962
|
+
const properties = {
|
|
2963
|
+
rag_id: id,
|
|
2964
|
+
content: doc.content || "",
|
|
2965
|
+
rag_metadata: JSON.stringify(metadata)
|
|
2966
|
+
};
|
|
2967
|
+
if (metadata.source !== void 0 && metadata.source !== null) {
|
|
2968
|
+
properties.rag_source = String(metadata.source);
|
|
2969
|
+
}
|
|
2970
|
+
return {
|
|
2971
|
+
class: this.collection,
|
|
2972
|
+
id: pointId(id),
|
|
2973
|
+
vector: doc.vector,
|
|
2974
|
+
properties
|
|
2975
|
+
};
|
|
2976
|
+
});
|
|
2977
|
+
await this._ensureCollection();
|
|
2978
|
+
if (this.dimensions && dimensions !== this.dimensions) {
|
|
2979
|
+
throw new Error(
|
|
2980
|
+
`WeaviateRetriever.store(): the documents have ${dimensions} dimensions, the collection has ${this.dimensions}`
|
|
2981
|
+
);
|
|
2982
|
+
}
|
|
2983
|
+
for (let i = 0; i < objects.length; i += UPSERT_BATCH) {
|
|
2984
|
+
const batch = objects.slice(i, i + UPSERT_BATCH);
|
|
2985
|
+
const results = await this._request("POST", "/batch/objects", {
|
|
2986
|
+
objects: batch
|
|
2987
|
+
});
|
|
2988
|
+
for (const [j, item] of (Array.isArray(results) ? results : []).entries()) {
|
|
2989
|
+
const detail = item && item.result && errorDetail(item.result.errors);
|
|
2990
|
+
if (!detail) continue;
|
|
2991
|
+
const id = batch[j] ? batch[j].properties.rag_id : "?";
|
|
2992
|
+
const kind = /vectors? with length/i.test(detail) ? "has the wrong vector dimensions" : "was not stored";
|
|
2993
|
+
throw new Error(
|
|
2994
|
+
this._redact(
|
|
2995
|
+
`WeaviateRetriever.store(): document "${id}" ${kind}: ${detail}`
|
|
2996
|
+
)
|
|
2997
|
+
);
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
this._exists = true;
|
|
3001
|
+
if (!this.dimensions) this.dimensions = dimensions;
|
|
3002
|
+
}
|
|
3003
|
+
/**
|
|
3004
|
+
* The documents most similar to a query vector.
|
|
3005
|
+
* @param {number[]|{ queryVector?: number[], topK?: number, signal?: AbortSignal }} request
|
|
3006
|
+
* A vector, or an object with one (required; optional in the type to
|
|
3007
|
+
* match the retriever contract)
|
|
3008
|
+
* @param {number} [k=10] - Results when `request` is a vector
|
|
3009
|
+
* @returns {Promise<Array<{ id: string, score: number, content: string, metadata: Object }>>}
|
|
3010
|
+
*/
|
|
3011
|
+
async retrieve(request, k = 10) {
|
|
3012
|
+
const {
|
|
3013
|
+
queryVector,
|
|
3014
|
+
topK = k,
|
|
3015
|
+
signal
|
|
3016
|
+
} = Array.isArray(request) ? { queryVector: request } : request || {};
|
|
3017
|
+
if (!Number.isInteger(topK) || topK < 1) {
|
|
3018
|
+
throw new Error(
|
|
3019
|
+
"WeaviateRetriever.retrieve(): topK must be a positive integer"
|
|
3020
|
+
);
|
|
3021
|
+
}
|
|
3022
|
+
checkVector(queryVector, "query");
|
|
3023
|
+
throwIfAborted(signal);
|
|
3024
|
+
if (!await this._collectionExists(signal)) return [];
|
|
3025
|
+
const data = await this._graphql(
|
|
3026
|
+
`{ Get { ${this.collection}(nearVector: { vector: ${JSON.stringify(queryVector)} }, limit: ${topK}, where: ${OURS_GRAPHQL}) { rag_id content rag_metadata _additional { distance } } } }`,
|
|
3027
|
+
signal
|
|
3028
|
+
);
|
|
3029
|
+
throwIfAborted(signal);
|
|
3030
|
+
const hits = data.Get && data.Get[this.collection] || [];
|
|
3031
|
+
return hits.map((hit) => ({
|
|
3032
|
+
id: hit.rag_id,
|
|
3033
|
+
score: 1 - hit._additional.distance,
|
|
3034
|
+
content: hit.content || "",
|
|
3035
|
+
metadata: parseMetadata(hit.rag_metadata)
|
|
3036
|
+
}));
|
|
3037
|
+
}
|
|
3038
|
+
/**
|
|
3039
|
+
* Deletes the matching objects, a server-limited round at a time.
|
|
3040
|
+
* @private
|
|
3041
|
+
*/
|
|
3042
|
+
async _deleteWhere(where) {
|
|
3043
|
+
if (!await this._collectionExists()) return;
|
|
3044
|
+
for (; ; ) {
|
|
3045
|
+
const json = await this._request("DELETE", "/batch/objects", {
|
|
3046
|
+
match: { class: this.collection, where },
|
|
3047
|
+
output: "minimal"
|
|
3048
|
+
});
|
|
3049
|
+
const r = json && json.results || {};
|
|
3050
|
+
if (r.failed > 0) {
|
|
3051
|
+
throw new Error(
|
|
3052
|
+
`WeaviateRetriever: ${r.failed} objects could not be deleted`
|
|
3053
|
+
);
|
|
3054
|
+
}
|
|
3055
|
+
if (!(r.limit > 0 && r.matches > r.limit && r.successful > 0)) return;
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
/**
|
|
3059
|
+
* Deletes the chunks of one source (`metadata.source`), for re-ingesting.
|
|
3060
|
+
* @param {string} source
|
|
3061
|
+
*/
|
|
3062
|
+
async removeBySource(source) {
|
|
3063
|
+
await this._deleteWhere({
|
|
3064
|
+
path: ["rag_source"],
|
|
3065
|
+
operator: "Equal",
|
|
3066
|
+
valueText: String(source)
|
|
3067
|
+
});
|
|
3068
|
+
}
|
|
3069
|
+
/**
|
|
3070
|
+
* Deletes documents by id.
|
|
3071
|
+
* @param {string[]} ids
|
|
3072
|
+
*/
|
|
3073
|
+
async delete(ids) {
|
|
3074
|
+
for (let i = 0; i < ids.length; i += DELETE_BATCH) {
|
|
3075
|
+
await this._deleteWhere({
|
|
3076
|
+
path: ["rag_id"],
|
|
3077
|
+
operator: "ContainsAny",
|
|
3078
|
+
valueTextArray: ids.slice(i, i + DELETE_BATCH).map(String)
|
|
3079
|
+
});
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
/** Deletes every document this class stored; the collection stays. */
|
|
3083
|
+
async clear() {
|
|
3084
|
+
await this._deleteWhere(OURS);
|
|
3085
|
+
}
|
|
3086
|
+
/** @returns {Promise<number>} */
|
|
3087
|
+
async size() {
|
|
3088
|
+
if (!await this._collectionExists()) return 0;
|
|
3089
|
+
const data = await this._graphql(
|
|
3090
|
+
`{ Aggregate { ${this.collection}(where: ${OURS_GRAPHQL}) { meta { count } } } }`
|
|
3091
|
+
);
|
|
3092
|
+
const rows = data.Aggregate && data.Aggregate[this.collection] || [];
|
|
3093
|
+
return rows[0] && rows[0].meta ? rows[0].meta.count : 0;
|
|
3094
|
+
}
|
|
3095
|
+
/**
|
|
3096
|
+
* Every document, without vectors.
|
|
3097
|
+
* @param {{ limit?: number }} [options]
|
|
3098
|
+
* @returns {Promise<Array<{ id: string, content: string, metadata: Object }>>}
|
|
3099
|
+
*/
|
|
3100
|
+
async list({ limit } = {}) {
|
|
3101
|
+
const out = [];
|
|
3102
|
+
if (!await this._collectionExists()) return out;
|
|
3103
|
+
let after = "";
|
|
3104
|
+
for (; ; ) {
|
|
3105
|
+
const page = await this._request(
|
|
3106
|
+
"GET",
|
|
3107
|
+
`/objects?class=${this.collection}&limit=${PAGE}&after=${encodeURIComponent(after)}`,
|
|
3108
|
+
void 0,
|
|
3109
|
+
{ allow404: true }
|
|
3110
|
+
);
|
|
3111
|
+
const objects = page && page.objects || [];
|
|
3112
|
+
for (const o of objects) {
|
|
3113
|
+
const p = o.properties || {};
|
|
3114
|
+
if (typeof p.rag_id !== "string") continue;
|
|
3115
|
+
out.push({
|
|
3116
|
+
id: p.rag_id,
|
|
3117
|
+
content: p.content || "",
|
|
3118
|
+
metadata: parseMetadata(p.rag_metadata)
|
|
3119
|
+
});
|
|
3120
|
+
if (limit && out.length >= limit) return out;
|
|
3121
|
+
}
|
|
3122
|
+
if (objects.length < PAGE) return out;
|
|
3123
|
+
after = objects[objects.length - 1].id;
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
};
|
|
3127
|
+
module2.exports = { WeaviateRetriever };
|
|
3128
|
+
}
|
|
3129
|
+
});
|
|
3130
|
+
|
|
2543
3131
|
// src/adapters/ai-sdk.js
|
|
2544
3132
|
var require_ai_sdk = __commonJS({
|
|
2545
3133
|
"src/adapters/ai-sdk.js"(exports2, module2) {
|
|
@@ -2700,6 +3288,7 @@ var require_connectors = __commonJS({
|
|
|
2700
3288
|
var { QdrantRetriever } = require_qdrant_retriever();
|
|
2701
3289
|
var { PineconeRetriever } = require_pinecone_retriever();
|
|
2702
3290
|
var { PgVectorRetriever } = require_pgvector_retriever();
|
|
3291
|
+
var { WeaviateRetriever } = require_weaviate_retriever();
|
|
2703
3292
|
var { createAiSdkAdapter } = require_ai_sdk();
|
|
2704
3293
|
module2.exports = {
|
|
2705
3294
|
OpenAIConnector,
|
|
@@ -2710,6 +3299,7 @@ var require_connectors = __commonJS({
|
|
|
2710
3299
|
QdrantRetriever,
|
|
2711
3300
|
PineconeRetriever,
|
|
2712
3301
|
PgVectorRetriever,
|
|
3302
|
+
WeaviateRetriever,
|
|
2713
3303
|
createAiSdkAdapter
|
|
2714
3304
|
};
|
|
2715
3305
|
}
|
|
@@ -2729,7 +3319,8 @@ var modules = {
|
|
|
2729
3319
|
"packages/connectors.js": () => require_connectors(),
|
|
2730
3320
|
"retrieval/pgvector-retriever.js": () => require_pgvector_retriever(),
|
|
2731
3321
|
"retrieval/pinecone-retriever.js": () => require_pinecone_retriever(),
|
|
2732
|
-
"retrieval/qdrant-retriever.js": () => require_qdrant_retriever()
|
|
3322
|
+
"retrieval/qdrant-retriever.js": () => require_qdrant_retriever(),
|
|
3323
|
+
"retrieval/weaviate-retriever.js": () => require_weaviate_retriever()
|
|
2733
3324
|
};
|
|
2734
3325
|
Object.defineProperty(mod, Symbol.for("@devilsdev/rag-connectors/internal"), {
|
|
2735
3326
|
value: (file) => {
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { OpenAIConnector, AnthropicConnector, CohereConnector, OllamaConnector, LocalEmbedder, QdrantRetriever, PineconeRetriever, PgVectorRetriever, createAiSdkAdapter } from "@devilsdev/rag-core/all-types";
|
|
1
|
+
export { OpenAIConnector, AnthropicConnector, CohereConnector, OllamaConnector, LocalEmbedder, QdrantRetriever, PineconeRetriever, PgVectorRetriever, WeaviateRetriever, createAiSdkAdapter } from "@devilsdev/rag-core/all-types";
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
2
|
const require = createRequire(import.meta.url);
|
|
3
3
|
const mod = require('./index.cjs');
|
|
4
|
-
export const { OpenAIConnector, AnthropicConnector, CohereConnector, OllamaConnector, LocalEmbedder, QdrantRetriever, PineconeRetriever, PgVectorRetriever, createAiSdkAdapter } = mod;
|
|
4
|
+
export const { OpenAIConnector, AnthropicConnector, CohereConnector, OllamaConnector, LocalEmbedder, QdrantRetriever, PineconeRetriever, PgVectorRetriever, WeaviateRetriever, createAiSdkAdapter } = mod;
|
|
5
5
|
export default mod;
|
package/package.json
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devilsdev/rag-connectors",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "Embedders, LLMs and vector stores for @devilsdev/rag-core: OpenAI, Anthropic, Cohere, Ollama, the AI SDK, Qdrant, pgvector and
|
|
3
|
+
"version": "3.7.1",
|
|
4
|
+
"description": "Embedders, LLMs and vector stores for @devilsdev/rag-core: OpenAI, Anthropic, Cohere, Ollama, the AI SDK, Qdrant, pgvector, Pinecone and Weaviate.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rag",
|
|
7
7
|
"openai",
|
|
8
8
|
"anthropic",
|
|
9
9
|
"ollama",
|
|
10
10
|
"qdrant",
|
|
11
|
-
"pgvector"
|
|
11
|
+
"pgvector",
|
|
12
|
+
"weaviate"
|
|
12
13
|
],
|
|
13
14
|
"license": "MIT",
|
|
14
15
|
"author": "Ali Kahwaji",
|
|
15
16
|
"homepage": "https://devilsdev.github.io/rag-pipeline-utils/",
|
|
16
17
|
"repository": {
|
|
17
18
|
"type": "git",
|
|
18
|
-
"url": "https://github.com/DevilsDev/rag-pipeline-utils.git"
|
|
19
|
+
"url": "git+https://github.com/DevilsDev/rag-pipeline-utils.git"
|
|
19
20
|
},
|
|
20
21
|
"bugs": {
|
|
21
22
|
"url": "https://github.com/DevilsDev/rag-pipeline-utils/issues"
|
|
@@ -44,7 +45,7 @@
|
|
|
44
45
|
"access": "public"
|
|
45
46
|
},
|
|
46
47
|
"dependencies": {
|
|
47
|
-
"@devilsdev/rag-core": "3.
|
|
48
|
+
"@devilsdev/rag-core": "3.7.1",
|
|
48
49
|
"axios": "^1.21.0"
|
|
49
50
|
},
|
|
50
51
|
"peerDependencies": {
|