redis-vl 0.1.0-beta.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.
Files changed (87) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +212 -0
  3. package/dist/errors.d.ts +50 -0
  4. package/dist/errors.d.ts.map +1 -0
  5. package/dist/errors.js +87 -0
  6. package/dist/errors.js.map +1 -0
  7. package/dist/index.d.ts +21 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +25 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/indexes/search-index.d.ts +368 -0
  12. package/dist/indexes/search-index.d.ts.map +1 -0
  13. package/dist/indexes/search-index.js +475 -0
  14. package/dist/indexes/search-index.js.map +1 -0
  15. package/dist/query/base.d.ts +51 -0
  16. package/dist/query/base.d.ts.map +1 -0
  17. package/dist/query/base.js +6 -0
  18. package/dist/query/base.js.map +1 -0
  19. package/dist/query/index.d.ts +7 -0
  20. package/dist/query/index.d.ts.map +1 -0
  21. package/dist/query/index.js +7 -0
  22. package/dist/query/index.js.map +1 -0
  23. package/dist/query/vector.d.ts +199 -0
  24. package/dist/query/vector.d.ts.map +1 -0
  25. package/dist/query/vector.js +198 -0
  26. package/dist/query/vector.js.map +1 -0
  27. package/dist/redis/index-info-parser.d.ts +25 -0
  28. package/dist/redis/index-info-parser.d.ts.map +1 -0
  29. package/dist/redis/index-info-parser.js +301 -0
  30. package/dist/redis/index-info-parser.js.map +1 -0
  31. package/dist/redis/schema-types.d.ts +81 -0
  32. package/dist/redis/schema-types.d.ts.map +1 -0
  33. package/dist/redis/schema-types.js +18 -0
  34. package/dist/redis/schema-types.js.map +1 -0
  35. package/dist/redis/utils.d.ts +7 -0
  36. package/dist/redis/utils.d.ts.map +1 -0
  37. package/dist/redis/utils.js +221 -0
  38. package/dist/redis/utils.js.map +1 -0
  39. package/dist/schema/fields.d.ts +163 -0
  40. package/dist/schema/fields.d.ts.map +1 -0
  41. package/dist/schema/fields.js +304 -0
  42. package/dist/schema/fields.js.map +1 -0
  43. package/dist/schema/index.d.ts +8 -0
  44. package/dist/schema/index.d.ts.map +1 -0
  45. package/dist/schema/index.js +8 -0
  46. package/dist/schema/index.js.map +1 -0
  47. package/dist/schema/schema.d.ts +247 -0
  48. package/dist/schema/schema.d.ts.map +1 -0
  49. package/dist/schema/schema.js +403 -0
  50. package/dist/schema/schema.js.map +1 -0
  51. package/dist/schema/types.d.ts +56 -0
  52. package/dist/schema/types.d.ts.map +1 -0
  53. package/dist/schema/types.js +62 -0
  54. package/dist/schema/types.js.map +1 -0
  55. package/dist/storage/base-storage.d.ts +107 -0
  56. package/dist/storage/base-storage.d.ts.map +1 -0
  57. package/dist/storage/base-storage.js +187 -0
  58. package/dist/storage/base-storage.js.map +1 -0
  59. package/dist/storage/hash-storage.d.ts +29 -0
  60. package/dist/storage/hash-storage.d.ts.map +1 -0
  61. package/dist/storage/hash-storage.js +122 -0
  62. package/dist/storage/hash-storage.js.map +1 -0
  63. package/dist/storage/index.d.ts +4 -0
  64. package/dist/storage/index.d.ts.map +1 -0
  65. package/dist/storage/index.js +4 -0
  66. package/dist/storage/index.js.map +1 -0
  67. package/dist/storage/json-storage.d.ts +19 -0
  68. package/dist/storage/json-storage.d.ts.map +1 -0
  69. package/dist/storage/json-storage.js +81 -0
  70. package/dist/storage/json-storage.js.map +1 -0
  71. package/dist/utils/distance.d.ts +92 -0
  72. package/dist/utils/distance.d.ts.map +1 -0
  73. package/dist/utils/distance.js +98 -0
  74. package/dist/utils/distance.js.map +1 -0
  75. package/dist/vectorizers/base-vectorizer.d.ts +88 -0
  76. package/dist/vectorizers/base-vectorizer.d.ts.map +1 -0
  77. package/dist/vectorizers/base-vectorizer.js +32 -0
  78. package/dist/vectorizers/base-vectorizer.js.map +1 -0
  79. package/dist/vectorizers/huggingface-vectorizer.d.ts +103 -0
  80. package/dist/vectorizers/huggingface-vectorizer.d.ts.map +1 -0
  81. package/dist/vectorizers/huggingface-vectorizer.js +142 -0
  82. package/dist/vectorizers/huggingface-vectorizer.js.map +1 -0
  83. package/dist/vectorizers/index.d.ts +10 -0
  84. package/dist/vectorizers/index.d.ts.map +1 -0
  85. package/dist/vectorizers/index.js +10 -0
  86. package/dist/vectorizers/index.js.map +1 -0
  87. package/package.json +96 -0
@@ -0,0 +1,81 @@
1
+ import { BaseStorage } from './base-storage.js';
2
+ import { SchemaValidationError } from '../errors.js';
3
+ /**
4
+ * Storage implementation for Redis JSON data type.
5
+ * Implements JSON-specific logic for validation and read/write operations.
6
+ */
7
+ export class JsonStorage extends BaseStorage {
8
+ constructor(schema) {
9
+ super(schema);
10
+ }
11
+ /**
12
+ * Write documents to Redis as JSON entries using pipelining for performance.
13
+ */
14
+ async write(client, data, options = {}) {
15
+ const { keys, ttl, batchSize = this.defaultBatchSize } = options;
16
+ // Validate inputs
17
+ if (data.length === 0) {
18
+ return [];
19
+ }
20
+ if (keys && keys.length !== data.length) {
21
+ throw new SchemaValidationError('Length of keys does not match the length of data');
22
+ }
23
+ // Pass 1: Preprocess and validate all documents
24
+ const preparedDocs = await this.preprocessAndValidateDocuments(data, options);
25
+ // Pass 2: Write all valid documents in batches using pipeline
26
+ const loadedKeys = [];
27
+ let pipeline = client.multi();
28
+ let commandCount = 0;
29
+ for (let i = 0; i < preparedDocs.length; i++) {
30
+ const { key, doc } = preparedDocs[i];
31
+ // Add JSON.SET command to pipeline
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ pipeline.json.set(key, '$', doc);
34
+ commandCount++;
35
+ // Add EXPIRE command if TTL is provided
36
+ if (ttl) {
37
+ pipeline.expire(key, ttl);
38
+ commandCount++;
39
+ }
40
+ loadedKeys.push(key);
41
+ // Execute pipeline in batches
42
+ if (commandCount >= batchSize || i === preparedDocs.length - 1) {
43
+ await pipeline.execAsPipeline();
44
+ pipeline = client.multi();
45
+ commandCount = 0;
46
+ }
47
+ }
48
+ return loadedKeys;
49
+ }
50
+ /**
51
+ * Read documents from Redis by keys using pipelining.
52
+ */
53
+ async get(client, keys, batchSize = this.defaultBatchSize) {
54
+ if (!keys || keys.length === 0) {
55
+ return [];
56
+ }
57
+ const results = [];
58
+ // Process in batches
59
+ for (let i = 0; i < keys.length; i += batchSize) {
60
+ const batch = keys.slice(i, i + batchSize);
61
+ const pipeline = client.multi();
62
+ // Add JSON.GET commands to pipeline
63
+ for (const key of batch) {
64
+ pipeline.json.get(key);
65
+ }
66
+ // Execute pipeline and get results
67
+ const batchResults = (await pipeline.execAsPipeline());
68
+ // Process batch results
69
+ for (const data of batchResults) {
70
+ if (data && typeof data === 'object') {
71
+ results.push(data);
72
+ }
73
+ else {
74
+ results.push(null);
75
+ }
76
+ }
77
+ }
78
+ return results;
79
+ }
80
+ }
81
+ //# sourceMappingURL=json-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-storage.js","sourceRoot":"","sources":["../../src/storage/json-storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Utility functions for normalizing vector distance metrics.
3
+ *
4
+ * Redis supports three distance metrics:
5
+ * - COSINE: Returns values between 0 and 2
6
+ * - L2 (Euclidean): Returns unbounded values (0 to infinity)
7
+ * - IP (Inner Product): Returns values determined by vector magnitude
8
+ *
9
+ * These functions normalize distances to similarity scores between 0 and 1:
10
+ * - 1.0 = perfect match (most similar)
11
+ * - 0.0 = completely different (least similar)
12
+ */
13
+ /**
14
+ * Normalize a COSINE distance to a similarity score between 0 and 1.
15
+ *
16
+ * COSINE distance in Redis ranges from 0 (identical vectors) to 2 (opposite vectors).
17
+ * This function converts it to a similarity score where:
18
+ * - distance 0 → similarity 1.0 (identical)
19
+ * - distance 1 → similarity 0.5 (orthogonal)
20
+ * - distance 2 → similarity 0.0 (opposite)
21
+ *
22
+ * Formula: similarity = (2 - distance) / 2
23
+ *
24
+ * @param distance - COSINE distance value from Redis (0 to 2)
25
+ * @returns Normalized similarity score (0 to 1)
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * normalizeCosineDistance(0) // 1.0 (identical)
30
+ * normalizeCosineDistance(1) // 0.5 (orthogonal)
31
+ * normalizeCosineDistance(2) // 0.0 (opposite)
32
+ * ```
33
+ */
34
+ export declare function normalizeCosineDistance(distance: number): number;
35
+ /**
36
+ * Denormalize a similarity score to a COSINE distance.
37
+ *
38
+ * This is the inverse of normalizeCosineDistance.
39
+ * Converts a similarity score (0 to 1) back to COSINE distance (0 to 2).
40
+ *
41
+ * Formula: distance = 2 - 2 * similarity
42
+ *
43
+ * @param similarity - Normalized similarity score (0 to 1)
44
+ * @returns COSINE distance value (0 to 2)
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * denormalizeCosineDistance(1.0) // 0 (identical)
49
+ * denormalizeCosineDistance(0.5) // 1 (orthogonal)
50
+ * denormalizeCosineDistance(0.0) // 2 (opposite)
51
+ * ```
52
+ */
53
+ export declare function denormalizeCosineDistance(similarity: number): number;
54
+ /**
55
+ * Normalize an L2 (Euclidean) distance to a similarity score between 0 and 1.
56
+ *
57
+ * L2 distance in Redis is unbounded (0 to infinity).
58
+ * This function converts it to a similarity score using the formula:
59
+ * similarity = 1 / (1 + distance)
60
+ *
61
+ * - distance 0 → similarity 1.0 (identical)
62
+ * - distance 1 → similarity 0.5
63
+ * - distance ∞ → similarity 0.0 (very different)
64
+ *
65
+ * @param distance - L2 distance value from Redis (0 to infinity)
66
+ * @returns Normalized similarity score (0 to 1)
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * normalizeL2Distance(0) // 1.0 (identical)
71
+ * normalizeL2Distance(1) // 0.5
72
+ * normalizeL2Distance(9) // 0.1
73
+ * normalizeL2Distance(99) // 0.01
74
+ * ```
75
+ */
76
+ export declare function normalizeL2Distance(distance: number): number;
77
+ /**
78
+ * Type definition for distance normalization functions.
79
+ */
80
+ export type DistanceNormalizer = (distance: number) => number;
81
+ /**
82
+ * Map of distance metrics to their normalization functions.
83
+ *
84
+ * - COSINE: Normalizes from [0, 2] to [0, 1]
85
+ * - L2: Normalizes from [0, ∞] to [0, 1]
86
+ * - IP: null (Inner Product is not normalized; use COSINE instead)
87
+ *
88
+ * Note: COSINE is normalized Inner Product by definition, so IP
89
+ * should not be normalized separately.
90
+ */
91
+ export declare const DISTANCE_NORMALIZERS: Record<string, DistanceNormalizer | null>;
92
+ //# sourceMappingURL=distance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distance.d.ts","sourceRoot":"","sources":["../../src/utils/distance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;AAE9D;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAI1E,CAAC"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Utility functions for normalizing vector distance metrics.
3
+ *
4
+ * Redis supports three distance metrics:
5
+ * - COSINE: Returns values between 0 and 2
6
+ * - L2 (Euclidean): Returns unbounded values (0 to infinity)
7
+ * - IP (Inner Product): Returns values determined by vector magnitude
8
+ *
9
+ * These functions normalize distances to similarity scores between 0 and 1:
10
+ * - 1.0 = perfect match (most similar)
11
+ * - 0.0 = completely different (least similar)
12
+ */
13
+ /**
14
+ * Normalize a COSINE distance to a similarity score between 0 and 1.
15
+ *
16
+ * COSINE distance in Redis ranges from 0 (identical vectors) to 2 (opposite vectors).
17
+ * This function converts it to a similarity score where:
18
+ * - distance 0 → similarity 1.0 (identical)
19
+ * - distance 1 → similarity 0.5 (orthogonal)
20
+ * - distance 2 → similarity 0.0 (opposite)
21
+ *
22
+ * Formula: similarity = (2 - distance) / 2
23
+ *
24
+ * @param distance - COSINE distance value from Redis (0 to 2)
25
+ * @returns Normalized similarity score (0 to 1)
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * normalizeCosineDistance(0) // 1.0 (identical)
30
+ * normalizeCosineDistance(1) // 0.5 (orthogonal)
31
+ * normalizeCosineDistance(2) // 0.0 (opposite)
32
+ * ```
33
+ */
34
+ export function normalizeCosineDistance(distance) {
35
+ return Math.max((2 - distance) / 2, 0);
36
+ }
37
+ /**
38
+ * Denormalize a similarity score to a COSINE distance.
39
+ *
40
+ * This is the inverse of normalizeCosineDistance.
41
+ * Converts a similarity score (0 to 1) back to COSINE distance (0 to 2).
42
+ *
43
+ * Formula: distance = 2 - 2 * similarity
44
+ *
45
+ * @param similarity - Normalized similarity score (0 to 1)
46
+ * @returns COSINE distance value (0 to 2)
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * denormalizeCosineDistance(1.0) // 0 (identical)
51
+ * denormalizeCosineDistance(0.5) // 1 (orthogonal)
52
+ * denormalizeCosineDistance(0.0) // 2 (opposite)
53
+ * ```
54
+ */
55
+ export function denormalizeCosineDistance(similarity) {
56
+ return Math.max(2 - 2 * similarity, 0);
57
+ }
58
+ /**
59
+ * Normalize an L2 (Euclidean) distance to a similarity score between 0 and 1.
60
+ *
61
+ * L2 distance in Redis is unbounded (0 to infinity).
62
+ * This function converts it to a similarity score using the formula:
63
+ * similarity = 1 / (1 + distance)
64
+ *
65
+ * - distance 0 → similarity 1.0 (identical)
66
+ * - distance 1 → similarity 0.5
67
+ * - distance ∞ → similarity 0.0 (very different)
68
+ *
69
+ * @param distance - L2 distance value from Redis (0 to infinity)
70
+ * @returns Normalized similarity score (0 to 1)
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * normalizeL2Distance(0) // 1.0 (identical)
75
+ * normalizeL2Distance(1) // 0.5
76
+ * normalizeL2Distance(9) // 0.1
77
+ * normalizeL2Distance(99) // 0.01
78
+ * ```
79
+ */
80
+ export function normalizeL2Distance(distance) {
81
+ return 1 / (1 + distance);
82
+ }
83
+ /**
84
+ * Map of distance metrics to their normalization functions.
85
+ *
86
+ * - COSINE: Normalizes from [0, 2] to [0, 1]
87
+ * - L2: Normalizes from [0, ∞] to [0, 1]
88
+ * - IP: null (Inner Product is not normalized; use COSINE instead)
89
+ *
90
+ * Note: COSINE is normalized Inner Product by definition, so IP
91
+ * should not be normalized separately.
92
+ */
93
+ export const DISTANCE_NORMALIZERS = {
94
+ COSINE: normalizeCosineDistance,
95
+ L2: normalizeL2Distance,
96
+ IP: null, // Normalized inner product is COSINE by definition
97
+ };
98
+ //# sourceMappingURL=distance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distance.js","sourceRoot":"","sources":["../../src/utils/distance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAgB;IACpD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,yBAAyB,CAAC,UAAkB;IACxD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,UAAU,EAAE,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAChD,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;AAC9B,CAAC;AAOD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA8C;IAC3E,MAAM,EAAE,uBAAuB;IAC/B,EAAE,EAAE,mBAAmB;IACvB,EAAE,EAAE,IAAI,EAAE,mDAAmD;CAChE,CAAC"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Base abstract class for all vectorizers.
3
+ *
4
+ * Vectorizers are responsible for converting text into numerical embeddings (vectors)
5
+ * that can be stored in Redis and used for semantic search.
6
+ *
7
+ * This class defines the interface that all concrete vectorizer implementations must follow.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * class MyVectorizer extends BaseVectorizer {
12
+ * async embed(text: string): Promise<number[]> {
13
+ * // Implementation
14
+ * }
15
+ *
16
+ * async embedMany(texts: string[], batchSize?: number): Promise<number[][]> {
17
+ * // Implementation
18
+ * }
19
+ *
20
+ * get dims(): number {
21
+ * return 384; // Embedding dimensions
22
+ * }
23
+ *
24
+ * get model(): string {
25
+ * return 'my-model-name';
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ export declare abstract class BaseVectorizer {
31
+ /**
32
+ * Generate an embedding for a single text.
33
+ *
34
+ * @param text - The text to embed
35
+ * @returns A promise that resolves to the embedding vector
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const embedding = await vectorizer.embed('Hello world');
40
+ * console.log(embedding); // [0.1, 0.2, 0.3, ...]
41
+ * ```
42
+ */
43
+ abstract embed(text: string): Promise<number[]>;
44
+ /**
45
+ * Generate embeddings for multiple texts.
46
+ *
47
+ * This method should be more efficient than calling embed() multiple times
48
+ * by batching requests to the underlying model.
49
+ *
50
+ * @param texts - Array of texts to embed
51
+ * @param batchSize - Optional batch size for processing (default: 32)
52
+ * @returns A promise that resolves to an array of embedding vectors
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const embeddings = await vectorizer.embedMany(['Hello', 'World']);
57
+ * console.log(embeddings); // [[0.1, 0.2, ...], [0.3, 0.4, ...]]
58
+ * ```
59
+ */
60
+ abstract embedMany(texts: string[], batchSize?: number): Promise<number[][]>;
61
+ /**
62
+ * Get the dimensionality of the embeddings produced by this vectorizer.
63
+ *
64
+ * This should match the length of the arrays returned by embed() and embedMany().
65
+ *
66
+ * @returns The number of dimensions in the embedding vectors
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * console.log(vectorizer.dims); // 384
71
+ * const embedding = await vectorizer.embed('test');
72
+ * console.log(embedding.length === vectorizer.dims); // true
73
+ * ```
74
+ */
75
+ abstract get dims(): number;
76
+ /**
77
+ * Get the name/identifier of the model used by this vectorizer.
78
+ *
79
+ * @returns The model name or identifier
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * console.log(vectorizer.model); // "sentence-transformers/all-MiniLM-L6-v2"
84
+ * ```
85
+ */
86
+ abstract get model(): string;
87
+ }
88
+ //# sourceMappingURL=base-vectorizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-vectorizer.d.ts","sourceRoot":"","sources":["../../src/vectorizers/base-vectorizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,8BAAsB,cAAc;IAChC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAE/C;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAE5E;;;;;;;;;;;;;OAaG;IACH,QAAQ,KAAK,IAAI,IAAI,MAAM,CAAC;IAE5B;;;;;;;;;OASG;IACH,QAAQ,KAAK,KAAK,IAAI,MAAM,CAAC;CAChC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Base abstract class for all vectorizers.
3
+ *
4
+ * Vectorizers are responsible for converting text into numerical embeddings (vectors)
5
+ * that can be stored in Redis and used for semantic search.
6
+ *
7
+ * This class defines the interface that all concrete vectorizer implementations must follow.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * class MyVectorizer extends BaseVectorizer {
12
+ * async embed(text: string): Promise<number[]> {
13
+ * // Implementation
14
+ * }
15
+ *
16
+ * async embedMany(texts: string[], batchSize?: number): Promise<number[][]> {
17
+ * // Implementation
18
+ * }
19
+ *
20
+ * get dims(): number {
21
+ * return 384; // Embedding dimensions
22
+ * }
23
+ *
24
+ * get model(): string {
25
+ * return 'my-model-name';
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ export class BaseVectorizer {
31
+ }
32
+ //# sourceMappingURL=base-vectorizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-vectorizer.js","sourceRoot":"","sources":["../../src/vectorizers/base-vectorizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,OAAgB,cAAc;CA4DnC"}
@@ -0,0 +1,103 @@
1
+ import { BaseVectorizer } from './base-vectorizer.js';
2
+ /**
3
+ * Configuration options for HuggingFaceVectorizer
4
+ */
5
+ export interface HuggingFaceConfig {
6
+ /**
7
+ * The Hugging Face model to use for embeddings.
8
+ *
9
+ * Examples:
10
+ * - 'Xenova/all-MiniLM-L6-v2' (384 dimensions, fast)
11
+ * - 'Xenova/all-mpnet-base-v2' (768 dimensions, better quality)
12
+ * - 'sentence-transformers/all-MiniLM-L6-v2'
13
+ */
14
+ model: string;
15
+ /**
16
+ * Device to run inference on.
17
+ * - 'cpu': Run on CPU (default)
18
+ * - 'webgpu': Run on GPU (if available)
19
+ */
20
+ device?: 'cpu' | 'webgpu';
21
+ /**
22
+ * Data type for model weights.
23
+ * - 'fp32': 32-bit floating point (default, best quality)
24
+ * - 'fp16': 16-bit floating point (faster, less memory)
25
+ * - 'q8': 8-bit quantized (fastest, smallest)
26
+ */
27
+ dtype?: 'fp32' | 'fp16' | 'q8';
28
+ /**
29
+ * Pooling strategy for combining token embeddings.
30
+ * - 'mean': Average all token embeddings (default)
31
+ * - 'cls': Use [CLS] token embedding
32
+ */
33
+ pooling?: 'mean' | 'cls';
34
+ /**
35
+ * Whether to normalize embeddings to unit length (L2 norm = 1).
36
+ * Default: true
37
+ */
38
+ normalize?: boolean;
39
+ }
40
+ /**
41
+ * HuggingFace vectorizer using Transformers.js for local inference.
42
+ *
43
+ * This vectorizer uses the @huggingface/transformers library to generate
44
+ * embeddings locally without requiring API keys or external services.
45
+ *
46
+ * Models are automatically downloaded and cached on first use.
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * import { HuggingFaceVectorizer } from 'redis-vl';
51
+ *
52
+ * const vectorizer = new HuggingFaceVectorizer({
53
+ * model: 'Xenova/all-MiniLM-L6-v2'
54
+ * });
55
+ *
56
+ * // Generate single embedding
57
+ * const embedding = await vectorizer.embed('Hello world');
58
+ * console.log(embedding.length); // 384
59
+ *
60
+ * // Generate multiple embeddings
61
+ * const embeddings = await vectorizer.embedMany(['Hello', 'World']);
62
+ * console.log(embeddings.length); // 2
63
+ * ```
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * // Use with preprocessing
68
+ * await index.load(documents, {
69
+ * preprocess: async (doc) => ({
70
+ * ...doc,
71
+ * embedding: await vectorizer.embed(doc.text)
72
+ * })
73
+ * });
74
+ * ```
75
+ */
76
+ export declare class HuggingFaceVectorizer extends BaseVectorizer {
77
+ private readonly config;
78
+ private pipelineInstance;
79
+ private modelDims;
80
+ constructor(config: HuggingFaceConfig);
81
+ /**
82
+ * Lazy load the Transformers.js pipeline.
83
+ * This ensures the library is only loaded when actually needed.
84
+ */
85
+ private loadPipeline;
86
+ /**
87
+ * Generate an embedding for a single text.
88
+ */
89
+ embed(text: string): Promise<number[]>;
90
+ /**
91
+ * Generate embeddings for multiple texts.
92
+ */
93
+ embedMany(texts: string[], batchSize?: number): Promise<number[][]>;
94
+ /**
95
+ * Get the dimensionality of the embeddings.
96
+ */
97
+ get dims(): number;
98
+ /**
99
+ * Get the model name.
100
+ */
101
+ get model(): string;
102
+ }
103
+ //# sourceMappingURL=huggingface-vectorizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"huggingface-vectorizer.d.ts","sourceRoot":"","sources":["../../src/vectorizers/huggingface-vectorizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC9B;;;;;;;OAOG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAE1B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAE/B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAEzB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,qBAAa,qBAAsB,SAAQ,cAAc;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,SAAS,CAAuB;gBAE5B,MAAM,EAAE,iBAAiB;IAiBrC;;;OAGG;YACW,YAAY;IA0B1B;;OAEG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAmB5C;;OAEG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAsC7E;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAQjB;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,MAAM,CAElB;CACJ"}
@@ -0,0 +1,142 @@
1
+ import { BaseVectorizer } from './base-vectorizer.js';
2
+ import { VectorizerError } from '../errors.js';
3
+ /**
4
+ * HuggingFace vectorizer using Transformers.js for local inference.
5
+ *
6
+ * This vectorizer uses the @huggingface/transformers library to generate
7
+ * embeddings locally without requiring API keys or external services.
8
+ *
9
+ * Models are automatically downloaded and cached on first use.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { HuggingFaceVectorizer } from 'redis-vl';
14
+ *
15
+ * const vectorizer = new HuggingFaceVectorizer({
16
+ * model: 'Xenova/all-MiniLM-L6-v2'
17
+ * });
18
+ *
19
+ * // Generate single embedding
20
+ * const embedding = await vectorizer.embed('Hello world');
21
+ * console.log(embedding.length); // 384
22
+ *
23
+ * // Generate multiple embeddings
24
+ * const embeddings = await vectorizer.embedMany(['Hello', 'World']);
25
+ * console.log(embeddings.length); // 2
26
+ * ```
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * // Use with preprocessing
31
+ * await index.load(documents, {
32
+ * preprocess: async (doc) => ({
33
+ * ...doc,
34
+ * embedding: await vectorizer.embed(doc.text)
35
+ * })
36
+ * });
37
+ * ```
38
+ */
39
+ export class HuggingFaceVectorizer extends BaseVectorizer {
40
+ config;
41
+ pipelineInstance = null;
42
+ modelDims = null;
43
+ constructor(config) {
44
+ super();
45
+ if (!config.model || config.model.trim() === '') {
46
+ throw new VectorizerError('Model name cannot be empty');
47
+ }
48
+ // Set defaults
49
+ this.config = {
50
+ model: config.model,
51
+ device: config.device ?? 'cpu',
52
+ dtype: config.dtype ?? 'fp32',
53
+ pooling: config.pooling ?? 'mean',
54
+ normalize: config.normalize ?? true,
55
+ };
56
+ }
57
+ /**
58
+ * Lazy load the Transformers.js pipeline.
59
+ * This ensures the library is only loaded when actually needed.
60
+ */
61
+ async loadPipeline() {
62
+ if (this.pipelineInstance) {
63
+ return this.pipelineInstance;
64
+ }
65
+ try {
66
+ const { pipeline } = await import('@huggingface/transformers');
67
+ this.pipelineInstance = await pipeline('feature-extraction', this.config.model, {
68
+ device: this.config.device,
69
+ dtype: this.config.dtype,
70
+ });
71
+ return this.pipelineInstance;
72
+ }
73
+ catch (error) {
74
+ if (error instanceof Error && error.message.includes('Cannot find module')) {
75
+ throw new VectorizerError('HuggingFaceVectorizer requires @huggingface/transformers. ' +
76
+ 'Install it with: npm install @huggingface/transformers', { cause: error });
77
+ }
78
+ throw error;
79
+ }
80
+ }
81
+ /**
82
+ * Generate an embedding for a single text.
83
+ */
84
+ async embed(text) {
85
+ const pipeline = await this.loadPipeline();
86
+ const output = await pipeline(text, {
87
+ pooling: this.config.pooling,
88
+ normalize: this.config.normalize,
89
+ });
90
+ // Extract the embedding from the output
91
+ const embedding = Array.from(output.data);
92
+ // Cache dimensions on first call
93
+ if (this.modelDims === null) {
94
+ this.modelDims = embedding.length;
95
+ }
96
+ return embedding;
97
+ }
98
+ /**
99
+ * Generate embeddings for multiple texts.
100
+ */
101
+ async embedMany(texts, batchSize = 32) {
102
+ if (texts.length === 0) {
103
+ return [];
104
+ }
105
+ const pipeline = await this.loadPipeline();
106
+ const results = [];
107
+ // Process in batches for memory efficiency
108
+ for (let i = 0; i < texts.length; i += batchSize) {
109
+ const batch = texts.slice(i, i + batchSize);
110
+ // Process batch
111
+ const batchOutputs = await Promise.all(batch.map((text) => pipeline(text, {
112
+ pooling: this.config.pooling,
113
+ normalize: this.config.normalize,
114
+ })));
115
+ // Extract embeddings from outputs
116
+ const batchEmbeddings = batchOutputs.map((output) => Array.from(output.data));
117
+ results.push(...batchEmbeddings);
118
+ }
119
+ // Cache dimensions on first call
120
+ if (this.modelDims === null && results.length > 0) {
121
+ this.modelDims = results[0].length;
122
+ }
123
+ return results;
124
+ }
125
+ /**
126
+ * Get the dimensionality of the embeddings.
127
+ */
128
+ get dims() {
129
+ if (this.modelDims === null) {
130
+ throw new VectorizerError('Embedding dimensions not yet determined. ' +
131
+ 'Call embed() or embedMany() first to initialize the model.');
132
+ }
133
+ return this.modelDims;
134
+ }
135
+ /**
136
+ * Get the model name.
137
+ */
138
+ get model() {
139
+ return this.config.model;
140
+ }
141
+ }
142
+ //# sourceMappingURL=huggingface-vectorizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"huggingface-vectorizer.js","sourceRoot":"","sources":["../../src/vectorizers/huggingface-vectorizer.ts"],"names":[],"mappings":"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"}