@remit/search-service 0.0.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/package.json +66 -0
- package/src/anchor.test.ts +164 -0
- package/src/anchor.ts +127 -0
- package/src/backends/bedrock.test.ts +148 -0
- package/src/backends/bedrock.ts +105 -0
- package/src/backends/memory.test.ts +168 -0
- package/src/backends/memory.ts +152 -0
- package/src/backends/pgvector.integ.test.ts +174 -0
- package/src/backends/pgvector.ts +306 -0
- package/src/backends/runtime-import.ts +16 -0
- package/src/backends/s3-vectors.test.ts +929 -0
- package/src/backends/s3-vectors.ts +383 -0
- package/src/backends/sqlite-vec.integ.test.ts +144 -0
- package/src/backends/sqlite-vec.ts +250 -0
- package/src/bedrock.ts +4 -0
- package/src/chunking/chunker.test.ts +79 -0
- package/src/chunking/chunker.ts +56 -0
- package/src/chunking/entities.test.ts +82 -0
- package/src/chunking/entities.ts +74 -0
- package/src/chunking/entropy.test.ts +98 -0
- package/src/chunking/entropy.ts +161 -0
- package/src/chunking/keys.ts +22 -0
- package/src/chunking/structured.test.ts +120 -0
- package/src/chunking/structured.ts +79 -0
- package/src/content-hash.test.ts +27 -0
- package/src/content-hash.ts +10 -0
- package/src/embeddings.test.ts +28 -0
- package/src/embeddings.ts +149 -0
- package/src/from-env.test.ts +62 -0
- package/src/from-env.ts +130 -0
- package/src/index.ts +71 -0
- package/src/pgvector.ts +4 -0
- package/src/s3-vectors.ts +5 -0
- package/src/search.test.ts +772 -0
- package/src/search.ts +395 -0
- package/src/semantic-search.integ.test.ts +130 -0
- package/src/sqlite-vec.ts +4 -0
- package/src/types.ts +155 -0
- package/tsconfig.json +8 -0
package/src/from-env.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { DataType } from "@huggingface/transformers";
|
|
2
|
+
import { BedrockEmbeddingService } from "./backends/bedrock.js";
|
|
3
|
+
import type { VectorStoreService } from "./backends/memory.js";
|
|
4
|
+
import { createMemoryVectorStore } from "./backends/memory.js";
|
|
5
|
+
import { createPgVectorStore } from "./backends/pgvector.js";
|
|
6
|
+
import { createS3VectorsBackend } from "./backends/s3-vectors.js";
|
|
7
|
+
import { createSqliteVectorStore } from "./backends/sqlite-vec.js";
|
|
8
|
+
import {
|
|
9
|
+
createDeterministicEmbeddingService,
|
|
10
|
+
createLocalEmbeddingService,
|
|
11
|
+
type EmbeddingService,
|
|
12
|
+
} from "./embeddings.js";
|
|
13
|
+
|
|
14
|
+
const parseDimensions = (): number | undefined => {
|
|
15
|
+
const raw = process.env.SEARCH_EMBEDDING_DIMENSIONS;
|
|
16
|
+
if (!raw) return undefined;
|
|
17
|
+
const parsed = Number(raw);
|
|
18
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`SEARCH_EMBEDDING_DIMENSIONS must be a positive integer, got: ${raw}`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return parsed;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const DTYPES: readonly DataType[] = [
|
|
27
|
+
"auto",
|
|
28
|
+
"fp32",
|
|
29
|
+
"fp16",
|
|
30
|
+
"q8",
|
|
31
|
+
"int8",
|
|
32
|
+
"uint8",
|
|
33
|
+
"q4",
|
|
34
|
+
"bnb4",
|
|
35
|
+
"q4f16",
|
|
36
|
+
"q2",
|
|
37
|
+
"q2f16",
|
|
38
|
+
"q1",
|
|
39
|
+
"q1f16",
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const parseDtype = (): DataType | undefined => {
|
|
43
|
+
const raw = process.env.SEARCH_EMBEDDING_DTYPE;
|
|
44
|
+
if (!raw) return undefined;
|
|
45
|
+
if (!DTYPES.includes(raw as DataType)) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`SEARCH_EMBEDDING_DTYPE must be one of ${DTYPES.join(", ")}, got: ${raw}`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return raw as DataType;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Select a vector store from the environment, shared by every process that
|
|
55
|
+
* composes a SearchService (the API, the search-index worker, the local
|
|
56
|
+
* indexing shim) so the selection rule lives in one place:
|
|
57
|
+
*
|
|
58
|
+
* - `DATA_BACKEND=postgres` + `PG_CONNECTION_URL` set → pgvector (Postgres parity).
|
|
59
|
+
* - `LOCAL_VECTORDB_PATH` set → persistent sqlite-vec (local dev).
|
|
60
|
+
* - `S3_VECTORS_BUCKET_NAME` + `S3_VECTORS_INDEX_NAME` set → S3 Vectors (prod).
|
|
61
|
+
* - otherwise → in-memory store (unit tests / default).
|
|
62
|
+
*
|
|
63
|
+
* `dimensions` should be the embedding service's dimension count. When a
|
|
64
|
+
* dimension-typed store is selected (sqlite-vec's vec0 table, pgvector's
|
|
65
|
+
* `VECTOR(n)` column), it is created with that dimension so the store and
|
|
66
|
+
* embedder always agree instead of failing confusingly at insert time (e.g. a
|
|
67
|
+
* 64-dim deterministic embedder writing into a 384-wide column).
|
|
68
|
+
*/
|
|
69
|
+
export const buildVectorStoreFromEnv = (
|
|
70
|
+
dimensions?: number,
|
|
71
|
+
): VectorStoreService => {
|
|
72
|
+
const pgConnectionUrl = process.env.PG_CONNECTION_URL;
|
|
73
|
+
if (process.env.DATA_BACKEND === "postgres" && pgConnectionUrl) {
|
|
74
|
+
return createPgVectorStore({
|
|
75
|
+
connectionString: pgConnectionUrl,
|
|
76
|
+
dimensions,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const localPath = process.env.LOCAL_VECTORDB_PATH;
|
|
80
|
+
if (localPath) {
|
|
81
|
+
return createSqliteVectorStore({ path: localPath, dimensions });
|
|
82
|
+
}
|
|
83
|
+
const bucket = process.env.S3_VECTORS_BUCKET_NAME;
|
|
84
|
+
const indexName = process.env.S3_VECTORS_INDEX_NAME;
|
|
85
|
+
if (bucket && indexName) {
|
|
86
|
+
return createS3VectorsBackend({
|
|
87
|
+
vectorBucketName: bucket,
|
|
88
|
+
indexName,
|
|
89
|
+
region: process.env.AWS_REGION,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return createMemoryVectorStore();
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Select an embedder from the environment, mirroring `buildVectorStoreFromEnv`:
|
|
97
|
+
*
|
|
98
|
+
* - `SEARCH_EMBEDDING_PROVIDER=local` → Transformers.js model (local dev). The
|
|
99
|
+
* model is `SEARCH_EMBEDDING_MODEL_ID` (default MiniLM); the Postgres-parity
|
|
100
|
+
* stack points it at a multilingual MiniLM so the ~50% non-English mail corpus
|
|
101
|
+
* embeds well. Both models are 384-dim, so the pgvector column is stable.
|
|
102
|
+
* - `SEARCH_EMBEDDING_PROVIDER=bedrock` → Bedrock Titan (prod).
|
|
103
|
+
* - otherwise → deterministic bag-of-words embedder (unit tests / default).
|
|
104
|
+
*
|
|
105
|
+
* `SEARCH_EMBEDDING_DIMENSIONS`, when set, pins the dimension count for the local
|
|
106
|
+
* and deterministic embedders so the store's vector column and the embedder
|
|
107
|
+
* agree regardless of which embedder a given process runs.
|
|
108
|
+
*
|
|
109
|
+
* `SEARCH_EMBEDDING_DTYPE`, when set, selects the ONNX weight precision the local
|
|
110
|
+
* model loads (`q8` → int8-quantized `model_quantized.onnx`); unset defaults to
|
|
111
|
+
* `fp32`. The search-index-worker container sets `q8` and bakes the matching file.
|
|
112
|
+
*/
|
|
113
|
+
export const buildEmbeddingServiceFromEnv = (): EmbeddingService => {
|
|
114
|
+
const provider = process.env.SEARCH_EMBEDDING_PROVIDER;
|
|
115
|
+
const dimensions = parseDimensions();
|
|
116
|
+
if (provider === "local") {
|
|
117
|
+
return createLocalEmbeddingService({
|
|
118
|
+
modelId: process.env.SEARCH_EMBEDDING_MODEL_ID,
|
|
119
|
+
dimensions,
|
|
120
|
+
dtype: parseDtype(),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (provider === "bedrock") {
|
|
124
|
+
return new BedrockEmbeddingService({
|
|
125
|
+
region: process.env.AWS_REGION,
|
|
126
|
+
modelId: process.env.SEARCH_EMBEDDING_MODEL_ID,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return createDeterministicEmbeddingService({ dimensions });
|
|
130
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export {
|
|
2
|
+
type AnchorBuildDeps,
|
|
3
|
+
type AnchorBuildParams,
|
|
4
|
+
type AnchorPayload,
|
|
5
|
+
buildAnchorSourceText,
|
|
6
|
+
buildMessageAnchor,
|
|
7
|
+
poolChunkVectors,
|
|
8
|
+
} from "./anchor.js";
|
|
9
|
+
export {
|
|
10
|
+
createMemoryVectorStore,
|
|
11
|
+
MemoryVectorStore,
|
|
12
|
+
type VectorStoreService,
|
|
13
|
+
} from "./backends/memory.js";
|
|
14
|
+
export {
|
|
15
|
+
type ChunkInput,
|
|
16
|
+
createEmailChunker,
|
|
17
|
+
type EmailChunker,
|
|
18
|
+
} from "./chunking/chunker.js";
|
|
19
|
+
export {
|
|
20
|
+
buildEntityChunks,
|
|
21
|
+
type ExtractedEntities,
|
|
22
|
+
extractEntities,
|
|
23
|
+
} from "./chunking/entities.js";
|
|
24
|
+
export {
|
|
25
|
+
buildBodyChunks,
|
|
26
|
+
shannonEntropy,
|
|
27
|
+
stripBoilerplate,
|
|
28
|
+
} from "./chunking/entropy.js";
|
|
29
|
+
export {
|
|
30
|
+
buildStructuredChunks,
|
|
31
|
+
extractAttachmentFileTypes,
|
|
32
|
+
} from "./chunking/structured.js";
|
|
33
|
+
export { computeContentHash } from "./content-hash.js";
|
|
34
|
+
export {
|
|
35
|
+
createDeterministicEmbeddingService,
|
|
36
|
+
createLocalEmbeddingService,
|
|
37
|
+
type DeterministicEmbeddingConfig,
|
|
38
|
+
DeterministicEmbeddingService,
|
|
39
|
+
type EmbeddingService,
|
|
40
|
+
type LocalEmbeddingConfig,
|
|
41
|
+
LocalEmbeddingService,
|
|
42
|
+
} from "./embeddings.js";
|
|
43
|
+
export {
|
|
44
|
+
createSearchService,
|
|
45
|
+
DefaultSearchService,
|
|
46
|
+
literalMatchScore,
|
|
47
|
+
rerank,
|
|
48
|
+
type SearchService,
|
|
49
|
+
type SearchServiceConfig,
|
|
50
|
+
tokenizeQuery,
|
|
51
|
+
type UpsertOptions,
|
|
52
|
+
type UpsertResult,
|
|
53
|
+
} from "./search.js";
|
|
54
|
+
export type {
|
|
55
|
+
AttachmentChunkInput,
|
|
56
|
+
Chunk,
|
|
57
|
+
ChunkMetadata,
|
|
58
|
+
ChunkType,
|
|
59
|
+
EnvelopeChunkAddress,
|
|
60
|
+
EnvelopeChunkInput,
|
|
61
|
+
IndexEmailParams,
|
|
62
|
+
ParsedBodyForChunking,
|
|
63
|
+
SearchIndexMessage,
|
|
64
|
+
SearchParams,
|
|
65
|
+
SearchResult,
|
|
66
|
+
VectorMatch,
|
|
67
|
+
VectorQuery,
|
|
68
|
+
VectorQueryFilter,
|
|
69
|
+
VectorRecord,
|
|
70
|
+
} from "./types.js";
|
|
71
|
+
export { searchIndexMessageSchema } from "./types.js";
|
package/src/pgvector.ts
ADDED