-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmanager.ts
More file actions
134 lines (120 loc) · 4.45 KB
/
Copy pathmanager.ts
File metadata and controls
134 lines (120 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/**
* EmbedderManager — encapsulates embedder resolution and lifecycle.
*
* Each instance maintains its own embedder cache and (optionally) its own
* transformers module loader, enabling true instance-level isolation for
* multi-tenant servers and test environments.
*/
import { Embedder } from './types';
import {
TransformersEmbedder,
loadTransformersModule,
resetTransformersModule,
createTransformersLoader,
} from './transformers';
import type { TransformersLoader } from './transformers';
// ---------------------------------------------------------------------------
// EmbedderManager
// ---------------------------------------------------------------------------
export interface EmbedderManagerOptions {
/**
* Custom transformers module loader for instance-level isolation.
* When omitted, uses the shared global loader (backward-compatible).
*/
transformersLoader?: TransformersLoader;
}
export class EmbedderManager {
private _defaultEmbedder: Embedder | null = null;
private readonly _loader: TransformersLoader;
constructor(options?: EmbedderManagerOptions) {
if (options?.transformersLoader) {
this._loader = options.transformersLoader;
} else {
// Default: use global shared loader (backward-compatible)
this._loader = {
load: loadTransformersModule,
reset: resetTransformersModule,
};
}
}
/**
* Create an EmbedderManager with a fully isolated transformers loader.
*
* The returned manager has its own module cache and failure state,
* completely independent of other managers and the global state.
*/
static createIsolated(): EmbedderManager {
const loader = createTransformersLoader();
return new EmbedderManager({ transformersLoader: loader });
}
/**
* Return a shared Embedder instance (async).
*
* Requires @huggingface/transformers to be installed and the model to be
* loadable. Throws an error when either condition is not met.
*/
async getEmbedder(): Promise<Embedder> {
if (this._defaultEmbedder) return this._defaultEmbedder;
const t = await this._loader.load();
if (!t) {
throw new Error(
'@huggingface/transformers is not installed. Semantic search requires a model-based embedder.\n' +
' Install it with:\n' +
' npm install @huggingface/transformers\n' +
' Then download the model:\n' +
' node scripts/download-model.mjs\n' +
' Or set mirror for China:\n' +
' export HF_ENDPOINT=https://hf-mirror.com',
);
}
try {
const probe = new TransformersEmbedder(() => this._loader.load());
await probe.embed('probe');
this._defaultEmbedder = probe;
} catch (err) {
throw new Error(
`Failed to load embedding model (bge-small-zh-v1.5): ${(err as Error).message?.split('\n')[0] ?? 'unknown'}\n` +
' To fix model download:\n' +
' 1. Set mirror: export HF_ENDPOINT=https://hf-mirror.com\n' +
' 2. Manual download: node scripts/download-model.mjs',
);
}
return this._defaultEmbedder;
}
/**
* Force-reset all cached state (useful for tests).
*
* Only resets the owned loader if this manager was created via
* `createIsolated()`. Global-loader managers delegate to the shared reset.
*/
reset(): void {
this._defaultEmbedder = null;
this._loader.reset();
}
}
// ---------------------------------------------------------------------------
// Global convenience functions (backward-compatible, prefer EmbedderManager)
// ---------------------------------------------------------------------------
/** Global manager instance for backward-compatible convenience functions. */
const _globalManager = new EmbedderManager();
/**
* Return a shared Embedder instance (async) via the global manager.
*
* @deprecated Prefer `new EmbedderManager().getEmbedder()` or pass an
* explicit embedder to `Context.create({ embedder })` to avoid hidden
* global state. This function remains for backward compatibility.
*
*/
export async function getEmbedder(): Promise<Embedder> {
return _globalManager.getEmbedder();
}
/**
* Force-reset the global cached default embedder (useful for tests).
*
* @deprecated Prefer `new EmbedderManager().reset()` on a manager instance
* to avoid affecting other Context instances. This function remains for
* backward compatibility and test cleanup.
*/
export function resetEmbedder(): void {
_globalManager.reset();
}