Skip to content

Commit d13502f

Browse files
authored
feat: Add support for RQ (#315)
* feat: add support for RQ * chore: update documentation Lint & format * ci: add 1.32 to test matrix and require it in the RQ test * chore: lint & format * ci: extend test matrix The previous commit only added a new env variable, it is not included in the test matrix automatically. v1.31 is now tested for node 22.x and v1.32 against the last 3 supported node versions. * test: update expected default configuration Add 'muvera' config for Weaviate 1.31+. Add 'rq' config for Weaviate 1.32+. Update expected error string * test: make the test independent of others This test fast failing with 'source not well formed' error message, which is Weaviate's speak for 'I dont think this property is a reference'. Still not sure why, but I figured it may have to do with how the test-suite is structured; namely, that each test relies on the state produced/left by the previous test. To test reference deletion, we instead simply create 2 objects, reference one from the other and then delete that reference. No external state. * chore: lint & format * refactor: re-use defined union type * chore: lint & format
1 parent d2c3b42 commit d13502f

File tree

10 files changed

+200
-53
lines changed

10 files changed

+200
-53
lines changed

.github/workflows/main.yaml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ env:
1515
WEAVIATE_129: 1.29.8
1616
WEAVIATE_130: 1.30.7
1717
WEAVIATE_131: 1.31.0
18+
WEAVIATE_132: 1.32.0-rc.1
1819

1920
concurrency:
2021
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -48,9 +49,11 @@ jobs:
4849
{ node: "22.x", weaviate: $WEAVIATE_127},
4950
{ node: "22.x", weaviate: $WEAVIATE_128},
5051
{ node: "22.x", weaviate: $WEAVIATE_129},
51-
{ node: "18.x", weaviate: $WEAVIATE_130},
52-
{ node: "20.x", weaviate: $WEAVIATE_130},
53-
{ node: "22.x", weaviate: $WEAVIATE_130}
52+
{ node: "22.x", weaviate: $WEAVIATE_130},
53+
{ node: "22.x", weaviate: $WEAVIATE_131},
54+
{ node: "18.x", weaviate: $WEAVIATE_132},
55+
{ node: "20.x", weaviate: $WEAVIATE_132},
56+
{ node: "22.x", weaviate: $WEAVIATE_132}
5457
]
5558
steps:
5659
- uses: actions/checkout@v3

src/collections/config/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
CollectionConfigUpdate,
2020
PQConfig,
2121
QuantizerConfig,
22+
RQConfig,
2223
SQConfig,
2324
VectorIndexConfig,
2425
VectorIndexConfigDynamic,
@@ -192,6 +193,9 @@ export class Quantizer {
192193
static isSQ(config?: QuantizerConfig): config is SQConfig {
193194
return config?.type === 'sq';
194195
}
196+
static isRQ(config?: QuantizerConfig): config is RQConfig {
197+
return config?.type === 'rq';
198+
}
195199
}
196200

197201
export const configGuards = {

src/collections/config/integration.test.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ import {
1212
VectorIndexConfigHNSW,
1313
} from './types/index.js';
1414

15-
const fail = (msg: string) => {
16-
throw new Error(msg);
17-
};
18-
1915
describe('Testing of the collection.config namespace', () => {
2016
let client: WeaviateClient;
2117

@@ -210,6 +206,30 @@ describe('Testing of the collection.config namespace', () => {
210206
expect(config.vectorizers.default.vectorizer.name).toEqual('none');
211207
});
212208

209+
requireAtLeast(1, 32, 0).it('should be able to get the config of a collection with hnsw-rq', async () => {
210+
const collectionName = 'TestCollectionConfigGetHNSWPlusRQ';
211+
const collection = await client.collections.create({
212+
name: collectionName,
213+
vectorizers: weaviate.configure.vectorizer.none({
214+
vectorIndexConfig: weaviate.configure.vectorIndex.hnsw({
215+
quantizer: weaviate.configure.vectorIndex.quantizer.rq(),
216+
}),
217+
}),
218+
});
219+
const config = await collection.config.get();
220+
221+
const vectorIndexConfig = config.vectorizers.default.indexConfig as VectorIndexConfigHNSW;
222+
expect(config.name).toEqual(collectionName);
223+
expect(config.generative).toBeUndefined();
224+
expect(config.reranker).toBeUndefined();
225+
expect(vectorIndexConfig).toBeDefined();
226+
expect(vectorIndexConfig.quantizer).toBeDefined();
227+
expect(vectorIndexConfig.quantizer?.type).toEqual('rq');
228+
expect(config.vectorizers.default.indexType).toEqual('hnsw');
229+
expect(config.vectorizers.default.properties).toBeUndefined();
230+
expect(config.vectorizers.default.vectorizer.name).toEqual('none');
231+
});
232+
213233
it('should be able to get the config of a collection with hnsw-bq', async () => {
214234
const collectionName = 'TestCollectionConfigGetHNSWPlusBQ';
215235
const query = () =>

src/collections/config/types/vectorIndex.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export type VectorIndexConfigHNSW = {
99
filterStrategy: VectorIndexFilterStrategy;
1010
flatSearchCutoff: number;
1111
maxConnections: number;
12-
quantizer: PQConfig | BQConfig | SQConfig | undefined;
12+
quantizer: QuantizerConfig | undefined;
1313
skip: boolean;
1414
vectorCacheMaxObjects: number;
1515
type: 'hnsw';
@@ -61,6 +61,12 @@ export type PQConfig = {
6161
type: 'pq';
6262
};
6363

64+
export type RQConfig = {
65+
bits?: number;
66+
rescoreLimit?: number;
67+
type: 'rq';
68+
};
69+
6470
export type PQEncoderConfig = {
6571
type: PQEncoderType;
6672
distribution: PQEncoderDistribution;
@@ -77,4 +83,4 @@ export type VectorIndexFilterStrategy = 'sweeping' | 'acorn';
7783

7884
export type VectorIndexConfig = VectorIndexConfigHNSW | VectorIndexConfigFlat | VectorIndexConfigDynamic;
7985

80-
export type QuantizerConfig = PQConfig | BQConfig | SQConfig;
86+
export type QuantizerConfig = PQConfig | BQConfig | SQConfig | RQConfig;

src/collections/config/utils.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import {
4646
PQEncoderType,
4747
PropertyConfig,
4848
PropertyVectorizerConfig,
49+
QuantizerConfig,
50+
RQConfig,
4951
ReferenceConfig,
5052
ReplicationConfig,
5153
Reranker,
@@ -182,6 +184,16 @@ export const parseVectorIndex = (module: ModuleConfig<VectorIndexType, VectorInd
182184
},
183185
};
184186
}
187+
if (QuantizerGuards.isRQCreate(quantizer)) {
188+
const { type, ...quant } = quantizer;
189+
return {
190+
...conf,
191+
rq: {
192+
...quant,
193+
enabled: true,
194+
},
195+
};
196+
}
185197
};
186198

187199
export const parseVectorizerConfig = (config?: VectorizerConfig): any => {
@@ -468,11 +480,13 @@ class ConfigMapping {
468480
throw new WeaviateDeserializationError(
469481
'Vector index vector cache max objects was not returned by Weaviate'
470482
);
471-
let quantizer: PQConfig | BQConfig | SQConfig | undefined;
483+
let quantizer: QuantizerConfig | undefined;
472484
if (exists<Record<string, any>>(v.pq) && v.pq.enabled === true) {
473485
quantizer = ConfigMapping.pq(v.pq);
474486
} else if (exists<Record<string, any>>(v.bq) && v.bq.enabled === true) {
475487
quantizer = ConfigMapping.bq(v.bq);
488+
} else if (exists<Record<string, any>>(v.rq) && v.rq.enabled === true) {
489+
quantizer = ConfigMapping.rq(v.rq);
476490
} else if (exists<Record<string, any>>(v.sq) && v.sq.enabled === true) {
477491
quantizer = ConfigMapping.sq(v.sq);
478492
} else {
@@ -508,6 +522,19 @@ class ConfigMapping {
508522
type: 'bq',
509523
};
510524
}
525+
static rq(v?: Record<string, unknown>): RQConfig | undefined {
526+
if (v === undefined) throw new WeaviateDeserializationError('RQ was not returned by Weaviate');
527+
if (!exists<boolean>(v.enabled))
528+
throw new WeaviateDeserializationError('RQ enabled was not returned by Weaviate');
529+
if (v.enabled === false) return undefined;
530+
const bits = v.bits === undefined ? 6 : (v.bits as number);
531+
const rescoreLimit = v.rescoreLimit === undefined ? 20 : (v.rescoreLimit as number);
532+
return {
533+
bits,
534+
rescoreLimit,
535+
type: 'rq',
536+
};
537+
}
511538
static sq(v?: Record<string, unknown>): SQConfig | undefined {
512539
if (v === undefined) throw new WeaviateDeserializationError('SQ was not returned by Weaviate');
513540
if (!exists<boolean>(v.enabled))

src/collections/configure/parsing.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {
33
BQConfigUpdate,
44
PQConfigCreate,
55
PQConfigUpdate,
6+
RQConfigCreate,
7+
RQConfigUpdate,
68
SQConfigCreate,
79
SQConfigUpdate,
810
} from './types/index.js';
@@ -13,7 +15,9 @@ type QuantizerConfig =
1315
| BQConfigCreate
1416
| BQConfigUpdate
1517
| SQConfigCreate
16-
| SQConfigUpdate;
18+
| SQConfigUpdate
19+
| RQConfigCreate
20+
| RQConfigUpdate;
1721

1822
export class QuantizerGuards {
1923
static isPQCreate(config?: QuantizerConfig): config is PQConfigCreate {
@@ -34,6 +38,12 @@ export class QuantizerGuards {
3438
static isSQUpdate(config?: QuantizerConfig): config is SQConfigUpdate {
3539
return (config as SQConfigUpdate)?.type === 'sq';
3640
}
41+
static isRQCreate(config?: QuantizerConfig): config is RQConfigCreate {
42+
return (config as RQConfigCreate)?.type === 'rq';
43+
}
44+
static isRQUpdate(config?: QuantizerConfig): config is RQConfigUpdate {
45+
return (config as RQConfigUpdate)?.type === 'rq';
46+
}
3747
}
3848

3949
export function parseWithDefault<D>(value: D | undefined, defaultValue: D): D {

src/collections/configure/types/vectorIndex.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
PQConfig,
55
PQEncoderDistribution,
66
PQEncoderType,
7+
RQConfig,
78
SQConfig,
89
VectorDistance,
910
VectorIndexConfigDynamic,
@@ -17,6 +18,13 @@ export type QuantizerRecursivePartial<T> = {
1718
[P in keyof T]: P extends 'type' ? T[P] : RecursivePartial<T[P]> | undefined;
1819
};
1920

21+
export type RQConfigCreate = QuantizerRecursivePartial<RQConfig>;
22+
23+
export type RQConfigUpdate = {
24+
rescoreLimit?: number;
25+
type: 'rq';
26+
};
27+
2028
export type PQConfigCreate = QuantizerRecursivePartial<PQConfig>;
2129

2230
export type PQConfigUpdate = {
@@ -59,7 +67,7 @@ export type VectorIndexConfigHNSWUpdate = {
5967
ef?: number;
6068
filterStrategy?: VectorIndexFilterStrategy;
6169
flatSearchCutoff?: number;
62-
quantizer?: PQConfigUpdate | BQConfigUpdate | SQConfigUpdate;
70+
quantizer?: PQConfigUpdate | BQConfigUpdate | SQConfigUpdate | RQConfigUpdate;
6371
vectorCacheMaxObjects?: number;
6472
};
6573

@@ -131,7 +139,7 @@ export type VectorIndexConfigHNSWCreateOptions = {
131139
/** The maximum number of connections. Default is 64. */
132140
maxConnections?: number;
133141
/** The quantizer configuration to use. Use `vectorIndex.quantizer.bq` or `vectorIndex.quantizer.pq` to make one. */
134-
quantizer?: PQConfigCreate | BQConfigCreate | SQConfigCreate;
142+
quantizer?: PQConfigCreate | BQConfigCreate | SQConfigCreate | RQConfigCreate;
135143
/** Whether to skip the index. Default is false. */
136144
skip?: boolean;
137145
/** The maximum number of objects to cache in the vector cache. Default is 1000000000000. */

src/collections/configure/vectorIndex.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
BQConfigUpdate,
1010
PQConfigCreate,
1111
PQConfigUpdate,
12+
RQConfigCreate,
13+
RQConfigUpdate,
1214
SQConfigCreate,
1315
SQConfigUpdate,
1416
VectorIndexConfigDynamicCreate,
@@ -65,7 +67,6 @@ const configure = {
6567
? {
6668
...rest,
6769
distance: distanceMetric,
68-
quantizer: rest.quantizer,
6970
}
7071
: undefined,
7172
};
@@ -111,12 +112,26 @@ const configure = {
111112
type: 'bq',
112113
};
113114
},
115+
/**
116+
* Create an object of type `RQConfigCreate` to be used when defining the quantizer configuration of a vector index.
117+
*
118+
* @param {number} [options.bits] Number of bits to user per vector element.
119+
* @param {number} [options.rescoreLimit] The rescore limit. Default is 1000.
120+
* @returns {RQConfigCreate} The object of type `RQConfigCreate`.
121+
*/
122+
rq: (options?: { bits?: number; rescoreLimit?: number }): RQConfigCreate => {
123+
return {
124+
bits: options?.bits,
125+
rescoreLimit: options?.rescoreLimit,
126+
type: 'rq',
127+
};
128+
},
114129
/**
115130
* Create an object of type `PQConfigCreate` to be used when defining the quantizer configuration of a vector index.
116131
*
117132
* @param {boolean} [options.bitCompression] Whether to use bit compression.
118-
* @param {number} [options.centroids] The number of centroids[.
119-
* @param {PQEncoderDistribution} ]options.encoder.distribution The encoder distribution.
133+
* @param {number} [options.centroids] The number of centroids.
134+
* @param {PQEncoderDistribution} [options.encoder.distribution] The encoder distribution.
120135
* @param {PQEncoderType} [options.encoder.type] The encoder type.
121136
* @param {number} [options.segments] The number of segments.
122137
* @param {number} [options.trainingLimit] The training limit.
@@ -194,7 +209,7 @@ const reconfigure = {
194209
* @param {number} [options.ef] The ef parameter. Default is -1.
195210
* @param {VectorIndexFilterStrategy} [options.filterStrategy] The filter strategy. Default is 'sweeping'.
196211
* @param {number} [options.flatSearchCutoff] The flat search cutoff. Default is 40000.
197-
* @param {PQConfigUpdate | BQConfigUpdate} [options.quantizer] The quantizer configuration to use. Use `vectorIndex.quantizer.bq` or `vectorIndex.quantizer.pq` to make one.
212+
* @param {PQConfigUpdate | BQConfigUpdate | SQConfigUpdate | RQConfigUpdate} [options.quantizer] The quantizer configuration to use. Use `vectorIndex.quantizer.bq` or `vectorIndex.quantizer.pq` to make one.
198213
* @param {number} [options.vectorCacheMaxObjects] The maximum number of objects to cache in the vector cache. Default is 1000000000000.
199214
* @returns {ModuleConfig<'hnsw', VectorIndexConfigHNSWUpdate>} The configuration object.
200215
*/
@@ -205,7 +220,7 @@ const reconfigure = {
205220
ef?: number;
206221
filterStrategy?: VectorIndexFilterStrategy;
207222
flatSearchCutoff?: number;
208-
quantizer?: PQConfigUpdate | BQConfigUpdate | SQConfigUpdate;
223+
quantizer?: PQConfigUpdate | BQConfigUpdate | SQConfigUpdate | RQConfigUpdate;
209224
vectorCacheMaxObjects?: number;
210225
}): ModuleConfig<'hnsw', VectorIndexConfigHNSWUpdate> => {
211226
return {
@@ -225,14 +240,29 @@ const reconfigure = {
225240
*
226241
* @param {boolean} [options.cache] Whether to cache the quantizer.
227242
* @param {number} [options.rescoreLimit] The new rescore limit.
228-
* @returns {BQConfigCreate} The configuration object.
243+
* @returns {BQConfigUpdate} The configuration object.
229244
*/
230245
bq: (options?: { cache?: boolean; rescoreLimit?: number }): BQConfigUpdate => {
231246
return {
232247
...options,
233248
type: 'bq',
234249
};
235250
},
251+
/**
252+
* Create an object of type `RQConfigUpdate` to be used when updating the quantizer configuration of a vector index.
253+
*
254+
* NOTE: If the vector index already has a quantizer configured, you cannot change its quantizer type; only its values.
255+
* So if you want to change the quantizer type, you must recreate the collection.
256+
*
257+
* @param {number} [options.rescoreLimit] The new rescore limit.
258+
* @returns {BQConfigUpdate} The configuration object.
259+
*/
260+
rq: (options?: { rescoreLimit?: number }): RQConfigUpdate => {
261+
return {
262+
...options,
263+
type: 'rq',
264+
};
265+
},
236266
/**
237267
* Create an object of type `PQConfigUpdate` to be used when updating the quantizer configuration of a vector index.
238268
*

0 commit comments

Comments
 (0)