@remit/search-service 0.0.19 → 0.0.20

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 (2) hide show
  1. package/package.json +5 -1
  2. package/src/capability.ts +51 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/search-service",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -9,6 +9,10 @@
9
9
  "types": "./src/index.ts",
10
10
  "default": "./src/index.ts"
11
11
  },
12
+ "./capability": {
13
+ "types": "./src/capability.ts",
14
+ "default": "./src/capability.ts"
15
+ },
12
16
  "./from-env": {
13
17
  "types": "./src/from-env.ts",
14
18
  "default": "./src/from-env.ts"
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Whether this process can embed at all, remembered per process.
3
+ *
4
+ * The backend and imap-worker container images ship without
5
+ * `@huggingface/transformers` (npm-scripts/docker-bundle.mjs,
6
+ * docker/runtime/backend/package.json), and the `off` provider embeds nothing by
7
+ * design. Both reach a caller as a throw carrying one of the codes below — a
8
+ * module that will not resolve, a native extension that will not load, or the
9
+ * typed {@link EmbeddingModelUnavailableError} / {@link EmbeddingDisabledError}.
10
+ *
11
+ * Every consumer that must degrade rather than crash classifies through here, so
12
+ * the vocabulary of "this deployment carries no embedding pipeline" is written
13
+ * once: the backend's `/search/semantic` gate
14
+ * (packages/backend/src/service/semantic-capability.ts, which adds the self-host
15
+ * scoping) and the filter pipeline's semantic skip
16
+ * (packages/mailbox-service/src/filters/pipeline.ts) both read the same flag, so
17
+ * one failed embed anywhere in the process is the whole probe.
18
+ */
19
+ const CAPABILITY_ABSENCE_CODES = new Set([
20
+ "ERR_MODULE_NOT_FOUND",
21
+ "MODULE_NOT_FOUND",
22
+ "ERR_DLOPEN_FAILED",
23
+ "ERR_EMBEDDING_MODEL_UNAVAILABLE",
24
+ ]);
25
+
26
+ let unavailable = false;
27
+
28
+ /** Whether a failure is the shape of an absent embedding pipeline. */
29
+ export const isEmbeddingCapabilityAbsence = (error: unknown): boolean => {
30
+ const code = (error as { code?: unknown } | null)?.code;
31
+ return typeof code === "string" && CAPABILITY_ABSENCE_CODES.has(code);
32
+ };
33
+
34
+ /** Whether an absence has already been observed in this process. */
35
+ export const isEmbeddingCapabilityUnavailable = (): boolean => unavailable;
36
+
37
+ /**
38
+ * Remember an observed absence, so later callers short-circuit instead of
39
+ * failing again. Returns false and records nothing for any other error, which is
40
+ * the caller's to rethrow.
41
+ */
42
+ export const recordEmbeddingCapabilityAbsence = (error: unknown): boolean => {
43
+ if (!isEmbeddingCapabilityAbsence(error)) return false;
44
+ unavailable = true;
45
+ return true;
46
+ };
47
+
48
+ /** Test-only reset for the memoized absence. */
49
+ export const _resetEmbeddingCapabilityForTest = (): void => {
50
+ unavailable = false;
51
+ };