@openspecui/server 3.11.1 → 3.11.2

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.
@@ -0,0 +1,3 @@
1
+ import { a as resolveCt2ModelDownloadPlanFromRepositoryFiles, i as CT2_REQUIRED_FILE_NAMES, n as createLocalCt2TranslatorFactory, r as CT2_OPTIONAL_FILE_NAMES, t as LocalCt2TranslatorFactory } from "./src-BJ-K9Dp2.mjs";
2
+
3
+ export { createLocalCt2TranslatorFactory };
@@ -0,0 +1,208 @@
1
+ import { join, posix } from "node:path";
2
+
3
+ //#region ../local-ct2-translator/src/ct2-download-plan.ts
4
+ const CT2_REQUIRED_FILE_NAMES = [
5
+ "config.json",
6
+ "model.bin",
7
+ "shared_vocabulary.json",
8
+ "source.spm",
9
+ "target.spm"
10
+ ];
11
+ const CT2_OPTIONAL_FILE_NAMES = ["tokenizer_config.json", "vocab.json"];
12
+ const CT2_OPTIONAL_FILE_NAME_SET = new Set(CT2_OPTIONAL_FILE_NAMES);
13
+ function resolveCt2ModelDownloadPlanFromRepositoryFiles(input) {
14
+ const normalizedFiles = dedupeFiles(input.files.filter((file) => file.path.trim().length > 0).map((file) => ({
15
+ ...file,
16
+ path: normalizePath(file.path)
17
+ })));
18
+ const fileByPath = new Map(normalizedFiles.map((file) => [file.path, file]));
19
+ const groups = collectCandidateRoots(normalizedFiles).map((rootDir) => createGroup(rootDir, fileByPath)).filter((group) => group !== null);
20
+ if (groups.length === 0) return null;
21
+ return buildPlan(input.modelId, groups, input.selectedGroupId);
22
+ }
23
+ function collectCandidateRoots(files) {
24
+ const roots = /* @__PURE__ */ new Set();
25
+ for (const file of files) {
26
+ if (posix.basename(file.path) !== "model.bin") continue;
27
+ roots.add(normalizeDir(posix.dirname(file.path)));
28
+ }
29
+ return [...roots];
30
+ }
31
+ function createGroup(rootDir, fileByPath) {
32
+ const requiredFiles = CT2_REQUIRED_FILE_NAMES.map((fileName) => fileByPath.get(joinRootFile(rootDir, fileName)));
33
+ if (requiredFiles.some((file) => file === void 0)) return null;
34
+ const concreteRequiredFiles = requiredFiles.filter((file) => file !== void 0);
35
+ const optionalFiles = CT2_OPTIONAL_FILE_NAMES.flatMap((fileName) => {
36
+ const file = fileByPath.get(joinRootFile(rootDir, fileName));
37
+ return file ? [file] : [];
38
+ });
39
+ const files = [...concreteRequiredFiles, ...optionalFiles].map((file) => toPlanFile(file));
40
+ const estimatedTotalBytes = files.reduce((total, file) => total + (file.sizeBytes ?? 0), 0);
41
+ const hasRequiredSizes = files.filter((file) => file.required).every((file) => file.sizeBytes !== void 0 && file.sizeBytes > 0);
42
+ return {
43
+ id: rootDir || "default",
44
+ label: rootDir ? posix.basename(rootDir) : "default",
45
+ description: rootDir ? `CTranslate2 artifacts from ${rootDir}.` : "CTranslate2 artifacts from the repository root.",
46
+ estimatedTotalBytes: estimatedTotalBytes > 0 ? estimatedTotalBytes : void 0,
47
+ selectable: hasRequiredSizes,
48
+ selected: false,
49
+ files
50
+ };
51
+ }
52
+ function toPlanFile(file) {
53
+ const required = !CT2_OPTIONAL_FILE_NAME_SET.has(posix.basename(file.path));
54
+ return {
55
+ path: file.path,
56
+ sizeBytes: file.sizeBytes,
57
+ required,
58
+ etag: file.etag,
59
+ revision: file.revision,
60
+ sourceUrl: file.sourceUrl,
61
+ raw: file.raw
62
+ };
63
+ }
64
+ function buildPlan(modelId, groups, selectedGroupId) {
65
+ const selectedGroup = selectRequestedGroup(groups, selectedGroupId) ?? selectDefaultGroup(groups) ?? groups[0];
66
+ const selectedId = selectedGroup.id;
67
+ const normalizedGroups = groups.map((group) => ({
68
+ ...group,
69
+ selected: group.id === selectedId,
70
+ files: [...group.files]
71
+ }));
72
+ return {
73
+ modelId,
74
+ estimatedTotalBytes: selectedGroup.estimatedTotalBytes,
75
+ files: [...selectedGroup.files],
76
+ selectedGroupId: selectedId,
77
+ groups: normalizedGroups
78
+ };
79
+ }
80
+ function selectRequestedGroup(groups, selectedGroupId) {
81
+ if (!selectedGroupId) return null;
82
+ return groups.find((group) => group.id === selectedGroupId) ?? null;
83
+ }
84
+ function selectDefaultGroup(groups) {
85
+ const selectableGroups = groups.filter((group) => group.selectable);
86
+ if (selectableGroups.length === 0) return null;
87
+ return [...selectableGroups].sort((left, right) => {
88
+ return (left.estimatedTotalBytes ?? Number.POSITIVE_INFINITY) - (right.estimatedTotalBytes ?? Number.POSITIVE_INFINITY) || left.id.localeCompare(right.id);
89
+ })[0] ?? null;
90
+ }
91
+ function joinRootFile(rootDir, fileName) {
92
+ return rootDir ? `${rootDir}/${fileName}` : fileName;
93
+ }
94
+ function normalizeDir(input) {
95
+ if (input === "." || input === "") return "";
96
+ return normalizePath(input);
97
+ }
98
+ function normalizePath(input) {
99
+ return input.replace(/^\.\/+/u, "").replace(/\/+/gu, "/");
100
+ }
101
+ function dedupeFiles(files) {
102
+ const deduped = /* @__PURE__ */ new Map();
103
+ for (const file of files) deduped.set(file.path, file);
104
+ return [...deduped.values()];
105
+ }
106
+
107
+ //#endregion
108
+ //#region ../local-ct2-translator/src/factory.ts
109
+ var LocalCt2TranslatorFactory = class {
110
+ constructor(options = {}) {
111
+ this.options = options;
112
+ }
113
+ async prepare(options) {
114
+ const model = options.model || this.options.defaultModel;
115
+ if (!model) throw new Error("A CT2 model id or runtime model path is required.");
116
+ const resolvedConfig = readRuntimeConfig(options.runtimeConfig);
117
+ const modelPath = resolveModelPath({
118
+ model,
119
+ cacheDir: this.options.cacheDir,
120
+ runtimeConfig: resolvedConfig
121
+ });
122
+ options.monitor?.setStatus({ message: `Loading CT2 model ${model}.` });
123
+ await createRuntimeTranslator(this.options, modelPath, resolvedConfig);
124
+ options.monitor?.setStatus({
125
+ message: `CT2 model ${model} is ready.`,
126
+ progress: 1
127
+ });
128
+ }
129
+ async create(options) {
130
+ const model = options.model || this.options.defaultModel;
131
+ if (!model) throw new Error("A CT2 model id or runtime model path is required.");
132
+ const resolvedConfig = readRuntimeConfig(options.runtimeConfig);
133
+ const modelPath = resolveModelPath({
134
+ model,
135
+ cacheDir: this.options.cacheDir,
136
+ runtimeConfig: resolvedConfig
137
+ });
138
+ options.monitor?.setStatus({ message: `Loading CT2 model ${model}.` });
139
+ const translator = await createRuntimeTranslator(this.options, modelPath, resolvedConfig);
140
+ options.monitor?.setStatus({
141
+ message: `CT2 model ${model} is ready.`,
142
+ progress: 1
143
+ });
144
+ return new LocalCt2Translator(translator, resolvedConfig, this.options);
145
+ }
146
+ };
147
+ function createLocalCt2TranslatorFactory(options = {}) {
148
+ return new LocalCt2TranslatorFactory(options);
149
+ }
150
+ var LocalCt2Translator = class {
151
+ constructor(translator, runtimeConfig, factoryOptions) {
152
+ this.translator = translator;
153
+ this.runtimeConfig = runtimeConfig;
154
+ this.factoryOptions = factoryOptions;
155
+ }
156
+ async *batchTranslate(inputs, options) {
157
+ throwIfAborted(options?.signal);
158
+ const result = await this.translator.translateBatch(inputs, {
159
+ beamSize: this.runtimeConfig.beamSize ?? this.factoryOptions.beamSize,
160
+ maxBatchSize: this.runtimeConfig.maxBatchSize ?? this.factoryOptions.maxBatchSize,
161
+ returnScores: false
162
+ });
163
+ throwIfAborted(options?.signal);
164
+ if (result.length !== inputs.length) throw new Error(`CT2 translator returned ${result.length} outputs for ${inputs.length} inputs.`);
165
+ for (const [index, entry] of result.entries()) yield {
166
+ index,
167
+ output: entry.text
168
+ };
169
+ }
170
+ };
171
+ async function createRuntimeTranslator(options, modelPath, runtimeConfig) {
172
+ return new (await ((options.loadModule ?? loadCt2RuntimeModule)())).Ct2Translator({
173
+ modelPath,
174
+ device: runtimeConfig.device ?? options.device,
175
+ threads: runtimeConfig.threads ?? options.threads
176
+ });
177
+ }
178
+ async function loadCt2RuntimeModule() {
179
+ return await import("ctranslate2");
180
+ }
181
+ function resolveModelPath(input) {
182
+ if (input.runtimeConfig.modelPath) return input.runtimeConfig.modelPath;
183
+ if (input.cacheDir) return join(input.cacheDir, "models", input.model);
184
+ return input.model;
185
+ }
186
+ function readRuntimeConfig(runtimeConfig) {
187
+ return {
188
+ modelPath: readString(runtimeConfig, "modelPath"),
189
+ device: readString(runtimeConfig, "device"),
190
+ threads: readNumber(runtimeConfig, "threads"),
191
+ beamSize: readNumber(runtimeConfig, "beamSize"),
192
+ maxBatchSize: readNumber(runtimeConfig, "maxBatchSize")
193
+ };
194
+ }
195
+ function readString(record, key) {
196
+ const value = record?.[key];
197
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
198
+ }
199
+ function readNumber(record, key) {
200
+ const value = record?.[key];
201
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
202
+ }
203
+ function throwIfAborted(signal) {
204
+ if (signal?.aborted) throw new DOMException("The operation was aborted.", "AbortError");
205
+ }
206
+
207
+ //#endregion
208
+ export { resolveCt2ModelDownloadPlanFromRepositoryFiles as a, CT2_REQUIRED_FILE_NAMES as i, createLocalCt2TranslatorFactory as n, CT2_OPTIONAL_FILE_NAMES as r, LocalCt2TranslatorFactory as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openspecui/server",
3
- "version": "3.11.1",
3
+ "version": "3.11.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.mjs",
6
6
  "exports": {
@@ -23,10 +23,9 @@
23
23
  "dependencies": {
24
24
  "@hono/node-server": "^1.14.1",
25
25
  "@huggingface/hub": "^2.12.0",
26
- "@huggingface/transformers": "^4.2.0",
27
26
  "@lydell/node-pty": "^1.1.0",
28
- "@openspecui/core": "3.11.1",
29
- "@openspecui/search": "3.11.1",
27
+ "@openspecui/core": "3.11.2",
28
+ "@openspecui/search": "3.11.2",
30
29
  "@trpc/server": "^11.0.0",
31
30
  "better-sqlite3": "^12.5.0",
32
31
  "hono": "^4.7.3",
@@ -34,6 +33,12 @@
34
33
  "ws": "^8.18.0",
35
34
  "zod": "^3.24.1"
36
35
  },
36
+ "// runtime-note": "These heavy runtimes are owned by the host package because translation engine installation is lifecycle-managed at the host boundary.",
37
+ "// bundle-note": "The translator engine packages keep runtime imports external in tsdown output, so this host package remains the install/detect truth during development runs.",
38
+ "optionalDependencies": {
39
+ "@huggingface/transformers": "~4.2.0",
40
+ "ctranslate2": "0.1.0"
41
+ },
37
42
  "devDependencies": {
38
43
  "@trpc/client": "^11.7.2",
39
44
  "@types/better-sqlite3": "^7.6.13",
File without changes
File without changes