preppergpt 0.1.0 → 0.1.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/README.md +24 -10
- package/compose/preppergpt.yaml +4 -2
- package/docs/bundles.md +30 -0
- package/docs/model-sources.md +2 -1
- package/installer/cli.mjs +33 -2
- package/installer/lib/bundles.mjs +107 -0
- package/installer/lib/render.mjs +13 -1
- package/package.json +2 -2
- package/profiles/models.json +14 -12
- package/services/local-scheduler/app.py +2 -2
package/README.md
CHANGED
|
@@ -11,22 +11,30 @@ working network during setup.
|
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Install from npm:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
node bin/preppergpt.js install --profile balanced
|
|
20
|
-
node bin/preppergpt.js start
|
|
17
|
+
npx preppergpt install --profile balanced
|
|
18
|
+
preppergpt start
|
|
21
19
|
```
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
Or install globally:
|
|
24
22
|
|
|
25
23
|
```bash
|
|
26
|
-
|
|
24
|
+
npm install -g preppergpt
|
|
25
|
+
preppergpt install --profile balanced
|
|
27
26
|
preppergpt start
|
|
28
27
|
```
|
|
29
28
|
|
|
29
|
+
GitHub source install:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/teamslop/preppergpt.git
|
|
33
|
+
cd preppergpt
|
|
34
|
+
node bin/preppergpt.js install --profile balanced
|
|
35
|
+
node bin/preppergpt.js start
|
|
36
|
+
```
|
|
37
|
+
|
|
30
38
|
Other profiles:
|
|
31
39
|
|
|
32
40
|
```bash
|
|
@@ -54,6 +62,7 @@ preppergpt stop
|
|
|
54
62
|
preppergpt status
|
|
55
63
|
preppergpt doctor
|
|
56
64
|
preppergpt switch-profile --profile speed
|
|
65
|
+
preppergpt bundle whisper
|
|
57
66
|
```
|
|
58
67
|
|
|
59
68
|
## Profiles
|
|
@@ -70,8 +79,13 @@ and route ordering into the generated compose override.
|
|
|
70
79
|
|
|
71
80
|
## Model Assets
|
|
72
81
|
|
|
73
|
-
|
|
74
|
-
|
|
82
|
+
PrepperGPT installs a bundled local Whisper Base STT cache during
|
|
83
|
+
`preppergpt install`. It is stored under `~/.preppergpt/data/models/whisper/base`
|
|
84
|
+
by default and mounted into OpenWebUI, so speech-to-text works from local files
|
|
85
|
+
after setup.
|
|
86
|
+
|
|
87
|
+
Some other routes can be pulled by the runtime, while very large routes such as
|
|
88
|
+
GLM 5.2 Q4 and Flux weights are marked as manual or external in
|
|
75
89
|
`profiles/models.json`. `preppergpt doctor` reports which selected routes still
|
|
76
90
|
need local files or endpoints.
|
|
77
91
|
|
|
@@ -84,7 +98,7 @@ npm publish --access public
|
|
|
84
98
|
```
|
|
85
99
|
|
|
86
100
|
Publishing requires an authenticated npm account with permission to publish the
|
|
87
|
-
|
|
101
|
+
`preppergpt` package.
|
|
88
102
|
|
|
89
103
|
The source repository is expected at:
|
|
90
104
|
|
package/compose/preppergpt.yaml
CHANGED
|
@@ -13,6 +13,7 @@ services:
|
|
|
13
13
|
hard: 1048576
|
|
14
14
|
volumes:
|
|
15
15
|
- ${PREPPERGPT_DATA_DIR:?set PREPPERGPT_DATA_DIR}/openwebui:/app/backend/data
|
|
16
|
+
- ${PREPPERGPT_MODELS_DIR:?set PREPPERGPT_MODELS_DIR}:/models:ro
|
|
16
17
|
- ../services/comfyui:/app/backend/data/parity-comfyui:ro
|
|
17
18
|
- ../themes/preppergpt/static/favicon.svg:/app/backend/open_webui/static/favicon.svg:ro
|
|
18
19
|
- ../themes/preppergpt/static/logo.svg:/app/backend/open_webui/static/logo.svg:ro
|
|
@@ -69,9 +70,10 @@ services:
|
|
|
69
70
|
CODE_INTERPRETER_JUPYTER_URL: "http://127.0.0.1:8888"
|
|
70
71
|
CODE_INTERPRETER_JUPYTER_AUTH: "token"
|
|
71
72
|
CODE_INTERPRETER_JUPYTER_AUTH_TOKEN: "${JUPYTER_TOKEN:?set JUPYTER_TOKEN}"
|
|
72
|
-
WHISPER_MODEL: "
|
|
73
|
+
WHISPER_MODEL: "${PREPPERGPT_WHISPER_MODEL_PATH:-/models/whisper/base}"
|
|
74
|
+
WHISPER_MODEL_DIR: "/models/whisper"
|
|
73
75
|
WHISPER_COMPUTE_TYPE: "int8"
|
|
74
|
-
WHISPER_MODEL_AUTO_UPDATE: "
|
|
76
|
+
WHISPER_MODEL_AUTO_UPDATE: "False"
|
|
75
77
|
WHISPER_VAD_FILTER: "True"
|
|
76
78
|
WHISPER_MULTILINGUAL: "True"
|
|
77
79
|
ENABLE_IMAGE_GENERATION: "True"
|
package/docs/bundles.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Bundles
|
|
2
|
+
|
|
3
|
+
PrepperGPT keeps npm lightweight but installs small always-on local assets
|
|
4
|
+
during setup.
|
|
5
|
+
|
|
6
|
+
## Whisper Base
|
|
7
|
+
|
|
8
|
+
`preppergpt install` downloads the MIT-licensed `Systran/faster-whisper-base`
|
|
9
|
+
CTranslate2 model into:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
~/.preppergpt/data/models/whisper/base
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
OpenWebUI receives:
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
WHISPER_MODEL=/models/whisper/base
|
|
19
|
+
WHISPER_MODEL_DIR=/models/whisper
|
|
20
|
+
WHISPER_MODEL_AUTO_UPDATE=False
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
To repair or refresh the bundle:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
preppergpt bundle whisper
|
|
27
|
+
preppergpt bundle whisper --force
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Source: https://huggingface.co/Systran/faster-whisper-base
|
package/docs/model-sources.md
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
PrepperGPT separates routing from model licensing and distribution.
|
|
4
4
|
|
|
5
5
|
- Ollama models are pulled by the local Ollama runtime when available.
|
|
6
|
-
-
|
|
6
|
+
- Whisper Base STT is installer-cached from `Systran/faster-whisper-base`
|
|
7
|
+
under the local PrepperGPT model directory and mounted into OpenWebUI.
|
|
7
8
|
- Hugging Face vision models are downloaded by the local vision sidecar.
|
|
8
9
|
- Very large GLM, Slopcode, and Flux assets are marked as manual or external
|
|
9
10
|
until a license-compatible public download source is configured.
|
package/installer/cli.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import http from "node:http";
|
|
3
|
+
import { ensureWhisperBundle, modelDirs, whisperBundleStatus } from "./lib/bundles.mjs";
|
|
3
4
|
import { detectMachine } from "./lib/detect.mjs";
|
|
4
5
|
import { buildPlan, normalizeProfile } from "./lib/planner.mjs";
|
|
5
6
|
import { packageRoot, runtimePaths } from "./lib/paths.mjs";
|
|
6
7
|
import { renderInstall } from "./lib/render.mjs";
|
|
7
8
|
import { commandResult, parseArgs, readJson, shellQuote } from "./lib/util.mjs";
|
|
8
9
|
|
|
9
|
-
const VERSION = "0.1.
|
|
10
|
+
const VERSION = "0.1.1";
|
|
10
11
|
|
|
11
12
|
function usage() {
|
|
12
13
|
return `PrepperGPT ${VERSION}
|
|
@@ -14,11 +15,12 @@ function usage() {
|
|
|
14
15
|
Usage:
|
|
15
16
|
preppergpt detect [--json]
|
|
16
17
|
preppergpt plan --profile balanced|intelligence|speed [--json]
|
|
17
|
-
preppergpt install --profile balanced|intelligence|speed [--dry-run] [--home PATH]
|
|
18
|
+
preppergpt install --profile balanced|intelligence|speed [--dry-run] [--skip-bundles] [--home PATH]
|
|
18
19
|
preppergpt start [--home PATH]
|
|
19
20
|
preppergpt stop [--home PATH]
|
|
20
21
|
preppergpt status [--home PATH] [--json]
|
|
21
22
|
preppergpt doctor [--profile balanced|intelligence|speed] [--home PATH]
|
|
23
|
+
preppergpt bundle whisper [--home PATH] [--force]
|
|
22
24
|
preppergpt switch-profile --profile balanced|intelligence|speed [--home PATH]
|
|
23
25
|
preppergpt version
|
|
24
26
|
`;
|
|
@@ -122,6 +124,11 @@ async function commandInstall(flags) {
|
|
|
122
124
|
return;
|
|
123
125
|
}
|
|
124
126
|
const paths = renderInstall(plan, detection, { home });
|
|
127
|
+
if (!flags.skip_bundles) {
|
|
128
|
+
console.log("Installing bundled Whisper base STT model...");
|
|
129
|
+
const bundle = await ensureWhisperBundle(paths.whisperHostDir, { force: Boolean(flags.force_bundle) });
|
|
130
|
+
console.log(`Whisper bundle: ${bundle.ready ? "ready" : "not ready"} at ${paths.whisperHostDir}`);
|
|
131
|
+
}
|
|
125
132
|
console.log(`Wrote ${paths.envFile}`);
|
|
126
133
|
console.log(`Wrote ${paths.generatedCompose}`);
|
|
127
134
|
console.log(`Wrote ${paths.modelPlan}`);
|
|
@@ -187,6 +194,7 @@ async function commandStatus(flags) {
|
|
|
187
194
|
}
|
|
188
195
|
|
|
189
196
|
async function commandDoctor(flags) {
|
|
197
|
+
const paths = runtimePaths(flags.home);
|
|
190
198
|
const detection = await detectMachine();
|
|
191
199
|
const plan = buildPlan(detection, profileFrom(flags));
|
|
192
200
|
printPlan(plan);
|
|
@@ -200,6 +208,28 @@ async function commandDoctor(flags) {
|
|
|
200
208
|
console.log(` port ${port}: occupied`);
|
|
201
209
|
}
|
|
202
210
|
}
|
|
211
|
+
const dirs = modelDirs(paths);
|
|
212
|
+
const whisper = whisperBundleStatus(dirs.whisperHostDir);
|
|
213
|
+
console.log(` whisper-base bundle: ${whisper.ready ? "ok" : `missing ${whisper.missing.length} files`} (${dirs.whisperHostDir})`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function commandBundle(flags, positional) {
|
|
217
|
+
const name = positional[1] || "whisper";
|
|
218
|
+
if (!["whisper", "whisper-base"].includes(name)) {
|
|
219
|
+
throw new Error(`Unknown bundle: ${name}`);
|
|
220
|
+
}
|
|
221
|
+
const paths = runtimePaths(flags.home);
|
|
222
|
+
const dirs = modelDirs(paths);
|
|
223
|
+
const bundle = await ensureWhisperBundle(dirs.whisperHostDir, {
|
|
224
|
+
force: Boolean(flags.force),
|
|
225
|
+
dryRun: Boolean(flags.dry_run)
|
|
226
|
+
});
|
|
227
|
+
console.log(`Whisper bundle ${bundle.ready ? "ready" : "not ready"} at ${dirs.whisperHostDir}`);
|
|
228
|
+
if (bundle.missing?.length) {
|
|
229
|
+
for (const file of bundle.missing) {
|
|
230
|
+
console.log(` missing ${file}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
203
233
|
}
|
|
204
234
|
|
|
205
235
|
export async function runCli(argv) {
|
|
@@ -220,6 +250,7 @@ export async function runCli(argv) {
|
|
|
220
250
|
if (command === "stop") return commandStop(flags);
|
|
221
251
|
if (command === "status") return commandStatus(flags);
|
|
222
252
|
if (command === "doctor") return commandDoctor(flags);
|
|
253
|
+
if (command === "bundle") return commandBundle(flags, positional);
|
|
223
254
|
if (command === "switch-profile") return commandSwitchProfile(flags);
|
|
224
255
|
throw new Error(`Unknown command: ${command}\n\n${usage()}`);
|
|
225
256
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Readable } from "node:stream";
|
|
4
|
+
import { pipeline } from "node:stream/promises";
|
|
5
|
+
import { readJson, writeJson } from "./util.mjs";
|
|
6
|
+
|
|
7
|
+
export const WHISPER_BUNDLE = {
|
|
8
|
+
id: "whisper-base",
|
|
9
|
+
name: "Whisper Base STT Bundle",
|
|
10
|
+
repo: "Systran/faster-whisper-base",
|
|
11
|
+
revision: "main",
|
|
12
|
+
license: "MIT",
|
|
13
|
+
modelPathInContainer: "/models/whisper/base",
|
|
14
|
+
files: ["config.json", "model.bin", "tokenizer.json", "vocabulary.txt", "README.md"],
|
|
15
|
+
description: "CTranslate2 faster-whisper conversion of openai/whisper-base for local OpenWebUI STT."
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function parseEnvFile(file) {
|
|
19
|
+
if (!fs.existsSync(file)) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
const entries = {};
|
|
23
|
+
for (const line of fs.readFileSync(file, "utf8").split(/\r?\n/)) {
|
|
24
|
+
if (!line || line.trim().startsWith("#") || !line.includes("=")) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const [key, ...valueParts] = line.split("=");
|
|
28
|
+
let value = valueParts.join("=");
|
|
29
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
30
|
+
value = value.slice(1, -1);
|
|
31
|
+
}
|
|
32
|
+
entries[key] = value;
|
|
33
|
+
}
|
|
34
|
+
return entries;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function modelDirs(paths) {
|
|
38
|
+
const env = parseEnvFile(paths.envFile);
|
|
39
|
+
const modelsDir = process.env.PREPPERGPT_MODELS_DIR || env.PREPPERGPT_MODELS_DIR || path.join(paths.dataDir, "models");
|
|
40
|
+
const whisperHostDir = path.join(modelsDir, "whisper", "base");
|
|
41
|
+
return { modelsDir, whisperHostDir };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function whisperBundleStatus(targetDir) {
|
|
45
|
+
const files = WHISPER_BUNDLE.files.map((file) => path.join(targetDir, file));
|
|
46
|
+
const missing = files.filter((file) => !fs.existsSync(file));
|
|
47
|
+
let manifest = null;
|
|
48
|
+
const manifestPath = path.join(targetDir, "preppergpt-bundle.json");
|
|
49
|
+
if (fs.existsSync(manifestPath)) {
|
|
50
|
+
try {
|
|
51
|
+
manifest = readJson(manifestPath);
|
|
52
|
+
} catch {
|
|
53
|
+
manifest = null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
id: WHISPER_BUNDLE.id,
|
|
58
|
+
targetDir,
|
|
59
|
+
ready: missing.length === 0,
|
|
60
|
+
missing,
|
|
61
|
+
manifest
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function downloadFile(url, targetFile) {
|
|
66
|
+
const response = await fetch(url, {
|
|
67
|
+
headers: {
|
|
68
|
+
"User-Agent": "preppergpt/0.1"
|
|
69
|
+
},
|
|
70
|
+
redirect: "follow"
|
|
71
|
+
});
|
|
72
|
+
if (!response.ok || !response.body) {
|
|
73
|
+
throw new Error(`Failed to download ${url}: HTTP ${response.status}`);
|
|
74
|
+
}
|
|
75
|
+
fs.mkdirSync(path.dirname(targetFile), { recursive: true });
|
|
76
|
+
const tmp = `${targetFile}.tmp-${process.pid}`;
|
|
77
|
+
await pipeline(Readable.fromWeb(response.body), fs.createWriteStream(tmp));
|
|
78
|
+
fs.renameSync(tmp, targetFile);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function ensureWhisperBundle(targetDir, options = {}) {
|
|
82
|
+
const status = whisperBundleStatus(targetDir);
|
|
83
|
+
if (status.ready && !options.force) {
|
|
84
|
+
return { ...status, changed: false };
|
|
85
|
+
}
|
|
86
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
87
|
+
if (options.dryRun) {
|
|
88
|
+
return { ...status, changed: false, dryRun: true };
|
|
89
|
+
}
|
|
90
|
+
for (const file of WHISPER_BUNDLE.files) {
|
|
91
|
+
const targetFile = path.join(targetDir, file);
|
|
92
|
+
if (fs.existsSync(targetFile) && !options.force) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const url = `https://huggingface.co/${WHISPER_BUNDLE.repo}/resolve/${WHISPER_BUNDLE.revision}/${file}`;
|
|
96
|
+
if (!options.quiet) {
|
|
97
|
+
console.log(`Downloading ${WHISPER_BUNDLE.repo}/${file}`);
|
|
98
|
+
}
|
|
99
|
+
await downloadFile(url, targetFile);
|
|
100
|
+
}
|
|
101
|
+
writeJson(path.join(targetDir, "preppergpt-bundle.json"), {
|
|
102
|
+
...WHISPER_BUNDLE,
|
|
103
|
+
installedAt: new Date().toISOString(),
|
|
104
|
+
source: `https://huggingface.co/${WHISPER_BUNDLE.repo}`
|
|
105
|
+
});
|
|
106
|
+
return { ...whisperBundleStatus(targetDir), changed: true };
|
|
107
|
+
}
|
package/installer/lib/render.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { WHISPER_BUNDLE } from "./bundles.mjs";
|
|
3
5
|
import { packagedPath, runtimePaths } from "./paths.mjs";
|
|
4
6
|
import { envQuote, writeJson, writeText } from "./util.mjs";
|
|
5
7
|
|
|
@@ -10,6 +12,7 @@ function secret(bytes = 24) {
|
|
|
10
12
|
function envFile(plan, paths, detection) {
|
|
11
13
|
const dataDir = process.env.PREPPERGPT_DATA_DIR || paths.dataDir;
|
|
12
14
|
const modelsDir = process.env.PREPPERGPT_MODELS_DIR || `${dataDir}/models`;
|
|
15
|
+
const whisperHostDir = path.join(modelsDir, "whisper", "base");
|
|
13
16
|
const adminPassword = process.env.PREPPERGPT_ADMIN_PASSWORD || secret(18);
|
|
14
17
|
const jupyterToken = process.env.JUPYTER_TOKEN || secret(18);
|
|
15
18
|
const searxngSecret = process.env.SEARXNG_SECRET_KEY || secret(24);
|
|
@@ -17,6 +20,9 @@ function envFile(plan, paths, detection) {
|
|
|
17
20
|
PREPPERGPT_PROFILE: plan.profile,
|
|
18
21
|
PREPPERGPT_DATA_DIR: dataDir,
|
|
19
22
|
PREPPERGPT_MODELS_DIR: modelsDir,
|
|
23
|
+
PREPPERGPT_WHISPER_HOST_DIR: whisperHostDir,
|
|
24
|
+
PREPPERGPT_WHISPER_MODEL: WHISPER_BUNDLE.id,
|
|
25
|
+
PREPPERGPT_WHISPER_MODEL_PATH: WHISPER_BUNDLE.modelPathInContainer,
|
|
20
26
|
PREPPERGPT_PORT: process.env.PREPPERGPT_PORT || "8080",
|
|
21
27
|
PREPPERGPT_DEFAULT_MODEL: plan.defaultModel,
|
|
22
28
|
PREPPERGPT_MODEL_ORDER_LIST: JSON.stringify(plan.routeIds),
|
|
@@ -60,17 +66,23 @@ function generatedCompose(plan, detection) {
|
|
|
60
66
|
|
|
61
67
|
export function renderInstall(plan, detection, options = {}) {
|
|
62
68
|
const paths = runtimePaths(options.home);
|
|
69
|
+
const dataDir = process.env.PREPPERGPT_DATA_DIR || paths.dataDir;
|
|
70
|
+
const modelsDir = process.env.PREPPERGPT_MODELS_DIR || `${dataDir}/models`;
|
|
71
|
+
const whisperHostDir = path.join(modelsDir, "whisper", "base");
|
|
63
72
|
fs.mkdirSync(paths.root, { recursive: true });
|
|
64
73
|
fs.mkdirSync(paths.dataDir, { recursive: true });
|
|
65
74
|
fs.mkdirSync(paths.composeDir, { recursive: true });
|
|
66
75
|
fs.mkdirSync(`${paths.dataDir}/preppergpt`, { recursive: true });
|
|
67
|
-
fs.mkdirSync(
|
|
76
|
+
fs.mkdirSync(modelsDir, { recursive: true });
|
|
77
|
+
fs.mkdirSync(whisperHostDir, { recursive: true });
|
|
68
78
|
writeText(paths.envFile, envFile(plan, paths, detection), 0o600);
|
|
69
79
|
writeText(paths.generatedCompose, generatedCompose(plan, detection));
|
|
70
80
|
writeJson(paths.modelPlan, plan);
|
|
71
81
|
writeJson(paths.detectReport, detection);
|
|
72
82
|
return {
|
|
73
83
|
...paths,
|
|
84
|
+
modelsDir,
|
|
85
|
+
whisperHostDir,
|
|
74
86
|
packageCompose: packagedPath("compose", "preppergpt.yaml")
|
|
75
87
|
};
|
|
76
88
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "preppergpt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A local-first ChatGPT-like field kit built on OpenWebUI and local models.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
21
|
"test": "node --test",
|
|
22
|
-
"check": "npm run test && node bin/preppergpt.js plan --profile balanced --json >/dev/null && node bin/preppergpt.js install --profile balanced --dry-run",
|
|
22
|
+
"check": "npm run test && node bin/preppergpt.js plan --profile balanced --json >/dev/null && node bin/preppergpt.js install --profile balanced --dry-run && node bin/preppergpt.js bundle whisper --dry-run >/dev/null",
|
|
23
23
|
"pack:dry-run": "npm pack --dry-run"
|
|
24
24
|
},
|
|
25
25
|
"keywords": [
|
package/profiles/models.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"agent": ["local-agent-glm52"],
|
|
14
14
|
"vision": ["local-vision-gemma4-12b", "local-vision-moondream2"],
|
|
15
15
|
"image": ["flux-2-klein-9b-fp8"],
|
|
16
|
-
"stt": ["whisper-
|
|
16
|
+
"stt": ["whisper-base-bundled"]
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"balanced": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"agent": ["local-agent-glm52"],
|
|
29
29
|
"vision": ["local-vision-gemma4-12b", "local-vision-moondream2"],
|
|
30
30
|
"image": ["flux-2-klein-9b-fp8"],
|
|
31
|
-
"stt": ["whisper-
|
|
31
|
+
"stt": ["whisper-base-bundled"]
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"speed": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"agent": ["local-agent-glm52"],
|
|
44
44
|
"vision": ["local-vision-moondream2", "local-vision-gemma4-12b"],
|
|
45
45
|
"image": ["flux-2-klein-9b-fp8"],
|
|
46
|
-
"stt": ["whisper-
|
|
46
|
+
"stt": ["whisper-base-bundled"]
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
},
|
|
@@ -256,21 +256,23 @@
|
|
|
256
256
|
}
|
|
257
257
|
},
|
|
258
258
|
{
|
|
259
|
-
"id": "whisper-
|
|
260
|
-
"name": "Whisper
|
|
259
|
+
"id": "whisper-base-bundled",
|
|
260
|
+
"name": "Bundled Whisper Base STT",
|
|
261
261
|
"roles": ["stt"],
|
|
262
262
|
"backend": "openwebui-faster-whisper",
|
|
263
263
|
"contextTokens": 0,
|
|
264
|
-
"qualityScore":
|
|
265
|
-
"speedScore":
|
|
266
|
-
"tpsEstimate": "
|
|
264
|
+
"qualityScore": 72,
|
|
265
|
+
"speedScore": 82,
|
|
266
|
+
"tpsEstimate": "local speech-to-text speed depends on CPU/GPU and clip length",
|
|
267
267
|
"requires": {
|
|
268
|
-
"minRamGb":
|
|
269
|
-
"diskGb":
|
|
268
|
+
"minRamGb": 8,
|
|
269
|
+
"diskGb": 1
|
|
270
270
|
},
|
|
271
271
|
"source": {
|
|
272
|
-
"type": "
|
|
273
|
-
"
|
|
272
|
+
"type": "bundled-download",
|
|
273
|
+
"model": "Systran/faster-whisper-base",
|
|
274
|
+
"license": "MIT",
|
|
275
|
+
"description": "Installer-cached faster-whisper base model mounted into OpenWebUI for offline local STT after install."
|
|
274
276
|
}
|
|
275
277
|
}
|
|
276
278
|
]
|
|
@@ -3792,7 +3792,7 @@ def local_parity_recommended_model(feature_family: str, primary_models: list[str
|
|
|
3792
3792
|
if "image understanding" in family:
|
|
3793
3793
|
return first_available(["local-vision-gemma4-12b", "local-vision-moondream2"])
|
|
3794
3794
|
if "voice" in family or "record mode" in family:
|
|
3795
|
-
return first_available(["whisper-large-v3", "local-agent-glm52"])
|
|
3795
|
+
return first_available(["whisper-base-bundled", "whisper-large-v3", "local-agent-glm52"])
|
|
3796
3796
|
if "shopping" in family:
|
|
3797
3797
|
return first_available(["glm52-shopping-research-local", "glm52-q4-local"])
|
|
3798
3798
|
if "job search" in family or "resume" in family or "finance" in family:
|
|
@@ -5317,7 +5317,7 @@ def local_parity_dashboard() -> dict:
|
|
|
5317
5317
|
{"id": "local-agent-glm52", "route": "local_agent", "best_for": "tool and agent workflows"},
|
|
5318
5318
|
{"id": "local-vision-gemma4-12b", "route": "local_vision", "best_for": "image understanding"},
|
|
5319
5319
|
{"id": "flux-2-klein-9b-fp8", "route": "comfyui_flux", "best_for": "image generation"},
|
|
5320
|
-
{"id": "whisper-
|
|
5320
|
+
{"id": "whisper-base-bundled", "route": "local_whisper_stt", "best_for": "speech-to-text"},
|
|
5321
5321
|
],
|
|
5322
5322
|
"route_profiles": {
|
|
5323
5323
|
key: {
|