dsh-pf-stream 0.2.4 → 0.3.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.
Files changed (80) hide show
  1. package/README.en.md +171 -34
  2. package/README.md +168 -31
  3. package/client.js +1193 -0
  4. package/icon.svg +18 -0
  5. package/lib/asr/catalog.d.ts +74 -0
  6. package/lib/asr/catalog.js +168 -0
  7. package/lib/asr/catalog.js.map +1 -0
  8. package/lib/asr/detect.d.ts +33 -0
  9. package/lib/asr/detect.js +117 -0
  10. package/lib/asr/detect.js.map +1 -0
  11. package/lib/asr/engine.d.ts +43 -0
  12. package/lib/asr/engine.js +143 -0
  13. package/lib/asr/engine.js.map +1 -0
  14. package/lib/asr/install.d.ts +56 -0
  15. package/lib/asr/install.js +317 -0
  16. package/lib/asr/install.js.map +1 -0
  17. package/lib/asr/verify.d.ts +22 -0
  18. package/lib/asr/verify.js +174 -0
  19. package/lib/asr/verify.js.map +1 -0
  20. package/lib/audio/acousticVad.d.ts +45 -0
  21. package/lib/audio/acousticVad.js +179 -0
  22. package/lib/audio/acousticVad.js.map +1 -0
  23. package/lib/audio/asrProviders.d.ts +51 -0
  24. package/lib/audio/asrProviders.js +251 -0
  25. package/lib/audio/asrProviders.js.map +1 -0
  26. package/lib/audio/asrRouter.d.ts +30 -0
  27. package/lib/audio/asrRouter.js +187 -0
  28. package/lib/audio/asrRouter.js.map +1 -0
  29. package/lib/audio/hallucinationGuard.d.ts +18 -0
  30. package/lib/audio/hallucinationGuard.js +97 -0
  31. package/lib/audio/hallucinationGuard.js.map +1 -0
  32. package/lib/audio/sidecar.d.ts +76 -0
  33. package/lib/audio/sidecar.js +362 -0
  34. package/lib/audio/sidecar.js.map +1 -0
  35. package/lib/audio/subtitleExtractor.d.ts +36 -0
  36. package/lib/audio/subtitleExtractor.js +218 -0
  37. package/lib/audio/subtitleExtractor.js.map +1 -0
  38. package/lib/audio/transcriptCache.d.ts +33 -0
  39. package/lib/audio/transcriptCache.js +126 -0
  40. package/lib/audio/transcriptCache.js.map +1 -0
  41. package/lib/audio/types.d.ts +136 -0
  42. package/lib/audio/types.js +10 -0
  43. package/lib/audio/types.js.map +1 -0
  44. package/lib/host/api.d.ts +23 -0
  45. package/lib/host/api.js +286 -0
  46. package/lib/host/api.js.map +1 -0
  47. package/lib/host/runtime.d.ts +167 -0
  48. package/lib/host/runtime.js +335 -0
  49. package/lib/host/runtime.js.map +1 -0
  50. package/lib/index.d.ts +229 -1
  51. package/lib/index.js +103 -2
  52. package/lib/index.js.map +1 -1
  53. package/lib/session/state.js +4 -0
  54. package/lib/session/state.js.map +1 -1
  55. package/lib/stage0/decoder.d.ts +1 -9
  56. package/lib/stage0/decoder.js +15 -45
  57. package/lib/stage0/decoder.js.map +1 -1
  58. package/lib/stage2/stateMachine.d.ts +4 -1
  59. package/lib/stage2/stateMachine.js +7 -1
  60. package/lib/stage2/stateMachine.js.map +1 -1
  61. package/lib/stage3/deterministicRouter.js +3 -3
  62. package/lib/stage3/deterministicRouter.js.map +1 -1
  63. package/lib/stage3/streamSampler.d.ts +9 -2
  64. package/lib/stage3/streamSampler.js +37 -16
  65. package/lib/stage3/streamSampler.js.map +1 -1
  66. package/lib/stage6/history.d.ts +2 -1
  67. package/lib/stage6/history.js +4 -1
  68. package/lib/stage6/history.js.map +1 -1
  69. package/lib/tool/handler.d.ts +9 -0
  70. package/lib/tool/handler.js +33 -3
  71. package/lib/tool/handler.js.map +1 -1
  72. package/lib/tool/schema.js +15 -2
  73. package/lib/tool/schema.js.map +1 -1
  74. package/lib/tool/streamCall.d.ts +11 -0
  75. package/lib/tool/streamCall.js +122 -9
  76. package/lib/tool/streamCall.js.map +1 -1
  77. package/lib/types.d.ts +9 -0
  78. package/locale/en.json +6 -0
  79. package/locale/zh.json +6 -0
  80. package/package.json +23 -2
package/icon.svg ADDED
@@ -0,0 +1,18 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="Video perception">
2
+ <defs>
3
+ <linearGradient id="pfFrame" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0" stop-color="#3b82f6"/>
5
+ <stop offset="1" stop-color="#7c3aed"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect x="4" y="12" width="56" height="40" rx="9" fill="url(#pfFrame)"/>
9
+ <rect x="4" y="12" width="56" height="40" rx="9" fill="none" stroke="#ffffff" stroke-opacity="0.28"/>
10
+ <g fill="#ffffff">
11
+ <rect x="20" y="26" width="3.5" height="12" rx="1.75"/>
12
+ <rect x="27" y="21" width="3.5" height="22" rx="1.75"/>
13
+ <rect x="34" y="24" width="3.5" height="16" rx="1.75"/>
14
+ <rect x="41" y="28" width="3.5" height="8" rx="1.75"/>
15
+ </g>
16
+ <circle cx="50" cy="45" r="9" fill="#0b1220"/>
17
+ <path d="M47.4 40.6 54 45l-6.6 4.4z" fill="#7cf5c4"/>
18
+ </svg>
@@ -0,0 +1,74 @@
1
+ /**
2
+ * ASR 目录表:面向用户的「服务商 / 本地引擎」预设。
3
+ *
4
+ * 设计意图(0.3.0):
5
+ * - 用户只回答三个问题:用不用云端、选哪家、填 Key;其余参数由预设给出。
6
+ * - 预设是纯数据,可被设置页展示、被宿主侧解析;不含密钥。
7
+ * - 协议只有两种(openai_verbose / openai_basic),服务商差异折叠为 baseUrl+model。
8
+ */
9
+ export type CloudPresetId = 'openai' | 'groq' | 'custom';
10
+ export interface CloudPreset {
11
+ id: CloudPresetId;
12
+ /** 展示名(设置页按 locale 覆盖文案,这里给兜底中文)。 */
13
+ label: string;
14
+ baseUrl: string;
15
+ model: string;
16
+ protocol: 'openai_verbose' | 'openai_basic';
17
+ /** 凭据 ref;每个服务商独立,切换不互相覆盖。 */
18
+ keyRef: string;
19
+ /** 用户获取 Key 的页面。 */
20
+ keyUrl: string;
21
+ /** 是否允许用户改写 baseUrl / model(自定义端点)。 */
22
+ editable: boolean;
23
+ }
24
+ export declare const CLOUD_PRESETS: readonly CloudPreset[];
25
+ export declare function cloudPreset(id: string | null | undefined): CloudPreset;
26
+ export type LocalEngineId = 'managed' | 'whisper_cpp' | 'whisper_ctranslate2' | 'openai_whisper';
27
+ export interface LocalEngineInfo {
28
+ id: LocalEngineId;
29
+ label: string;
30
+ /** PATH 上的候选可执行文件名(不含后缀)。 */
31
+ binNames: readonly string[];
32
+ /** 是否需要 ggml 模型文件(whisper.cpp 系);Python 封装自带模型管理。 */
33
+ needsModelFile: boolean;
34
+ }
35
+ export declare const LOCAL_ENGINES: readonly LocalEngineInfo[];
36
+ export type ModelPresetId = 'base-q5_1' | 'small-q5_1' | 'medium-q5_0';
37
+ export interface ModelPreset {
38
+ id: ModelPresetId;
39
+ label: string;
40
+ file: string;
41
+ /** 官方下载地址(Hugging Face);镜像在下载器里按顺序尝试。 */
42
+ path: string;
43
+ /** 建议磁盘占用(字节,用于下载前提示)。 */
44
+ approxBytes: number;
45
+ }
46
+ export declare const MODEL_PRESETS: readonly ModelPreset[];
47
+ export declare const DEFAULT_MODEL_ID: ModelPresetId;
48
+ export declare function modelPreset(id: string | null | undefined): ModelPreset;
49
+ /** Hugging Face 官方源 + 国内镜像;顺序即尝试顺序。 */
50
+ export declare function modelDownloadUrls(preset: ModelPreset): string[];
51
+ /** whisper.cpp 官方 Windows 预编译包(x64);文件名随发行变化,候选按序尝试。 */
52
+ export declare const WHISPER_CPP_VERSION = "v1.7.6";
53
+ export declare function whisperCppUrls(): string[];
54
+ /** 托管目录:<cacheDir>/asr/{bin,models}。 */
55
+ export declare function managedPaths(cacheDir: string): {
56
+ binDir: string;
57
+ modelDir: string;
58
+ exe: string;
59
+ };
60
+ export interface LocalArgsContext {
61
+ audioToken?: string;
62
+ dirToken?: string;
63
+ outBaseToken?: string;
64
+ modelPath: string | null;
65
+ modelName: string;
66
+ language: string | null;
67
+ threads: number;
68
+ }
69
+ /**
70
+ * 内置参数模板。用户不再手写 args:
71
+ * - whisper.cpp 系:`-m <model> -f <audio> -osrt -of <outbase> -l <lang> -t <threads>`
72
+ * - Python 封装:调用各自 CLI 的 srt 输出参数,音频作位置参数。
73
+ */
74
+ export declare function buildLocalArgs(engine: LocalEngineId, ctx: LocalArgsContext): string[];
@@ -0,0 +1,168 @@
1
+ /**
2
+ * ASR 目录表:面向用户的「服务商 / 本地引擎」预设。
3
+ *
4
+ * 设计意图(0.3.0):
5
+ * - 用户只回答三个问题:用不用云端、选哪家、填 Key;其余参数由预设给出。
6
+ * - 预设是纯数据,可被设置页展示、被宿主侧解析;不含密钥。
7
+ * - 协议只有两种(openai_verbose / openai_basic),服务商差异折叠为 baseUrl+model。
8
+ */
9
+ import { join } from 'node:path';
10
+ export const CLOUD_PRESETS = [
11
+ {
12
+ id: 'openai',
13
+ label: 'OpenAI',
14
+ baseUrl: 'https://api.openai.com',
15
+ model: 'whisper-1',
16
+ protocol: 'openai_verbose',
17
+ keyRef: 'DSH_ASR_KEY_OPENAI',
18
+ keyUrl: 'https://platform.openai.com/api-keys',
19
+ editable: false,
20
+ },
21
+ {
22
+ id: 'groq',
23
+ label: 'Groq',
24
+ baseUrl: 'https://api.groq.com/openai',
25
+ model: 'whisper-large-v3-turbo',
26
+ protocol: 'openai_verbose',
27
+ keyRef: 'DSH_ASR_KEY_GROQ',
28
+ keyUrl: 'https://console.groq.com/keys',
29
+ editable: false,
30
+ },
31
+ {
32
+ id: 'custom',
33
+ label: '自定义 OpenAI 兼容端点',
34
+ baseUrl: '',
35
+ model: '',
36
+ protocol: 'openai_verbose',
37
+ keyRef: 'DSH_ASR_KEY_CUSTOM',
38
+ keyUrl: '',
39
+ editable: true,
40
+ },
41
+ ];
42
+ export function cloudPreset(id) {
43
+ const found = CLOUD_PRESETS.find((preset) => preset.id === id);
44
+ return found ?? CLOUD_PRESETS[0];
45
+ }
46
+ export const LOCAL_ENGINES = [
47
+ { id: 'managed', label: '内置 whisper.cpp(插件托管)', binNames: [], needsModelFile: true },
48
+ { id: 'whisper_cpp', label: 'whisper.cpp(已安装)', binNames: ['whisper-cli', 'main'], needsModelFile: true },
49
+ {
50
+ id: 'whisper_ctranslate2',
51
+ label: 'faster-whisper / whisper-ctranslate2',
52
+ binNames: ['whisper-ctranslate2', 'faster-whisper'],
53
+ needsModelFile: false,
54
+ },
55
+ { id: 'openai_whisper', label: 'openai-whisper(Python)', binNames: ['whisper'], needsModelFile: false },
56
+ ];
57
+ export const MODEL_PRESETS = [
58
+ {
59
+ id: 'base-q5_1',
60
+ label: '快速(base,约 60MB)',
61
+ file: 'ggml-base-q5_1.bin',
62
+ path: 'ggerganov/whisper.cpp/resolve/main/ggml-base-q5_1.bin',
63
+ approxBytes: 60 * 1024 * 1024,
64
+ },
65
+ {
66
+ id: 'small-q5_1',
67
+ label: '推荐(small,约 190MB)',
68
+ file: 'ggml-small-q5_1.bin',
69
+ path: 'ggerganov/whisper.cpp/resolve/main/ggml-small-q5_1.bin',
70
+ approxBytes: 190 * 1024 * 1024,
71
+ },
72
+ {
73
+ id: 'medium-q5_0',
74
+ label: '更准(medium,约 540MB)',
75
+ file: 'ggml-medium-q5_0.bin',
76
+ path: 'ggerganov/whisper.cpp/resolve/main/ggml-medium-q5_0.bin',
77
+ approxBytes: 540 * 1024 * 1024,
78
+ },
79
+ ];
80
+ export const DEFAULT_MODEL_ID = 'small-q5_1';
81
+ export function modelPreset(id) {
82
+ const found = MODEL_PRESETS.find((preset) => preset.id === id);
83
+ if (found !== undefined)
84
+ return found;
85
+ return MODEL_PRESETS.find((preset) => preset.id === DEFAULT_MODEL_ID);
86
+ }
87
+ /** Hugging Face 官方源 + 国内镜像;顺序即尝试顺序。 */
88
+ export function modelDownloadUrls(preset) {
89
+ const override = process.env.DSH_PF_STREAM_MODEL_BASE?.trim();
90
+ const bases = override !== undefined && override !== '' ? [override] : [
91
+ 'https://huggingface.co',
92
+ 'https://hf-mirror.com',
93
+ ];
94
+ return bases.map((base) => `${base.replace(/\/+$/, '')}/${preset.path}`);
95
+ }
96
+ /** whisper.cpp 官方 Windows 预编译包(x64);文件名随发行变化,候选按序尝试。 */
97
+ export const WHISPER_CPP_VERSION = 'v1.7.6';
98
+ export function whisperCppUrls() {
99
+ const override = process.env.DSH_PF_STREAM_WHISPER_URL?.trim();
100
+ if (override !== undefined && override !== '')
101
+ return [override];
102
+ const base = `https://github.com/ggml-org/whisper.cpp/releases/download/${WHISPER_CPP_VERSION}`;
103
+ return [`${base}/whisper-bin-x64.zip`];
104
+ }
105
+ /** 托管目录:<cacheDir>/asr/{bin,models}。 */
106
+ export function managedPaths(cacheDir) {
107
+ const exe = process.platform === 'win32' ? 'whisper-cli.exe' : 'whisper-cli';
108
+ const binDir = join(cacheDir, 'asr', 'bin');
109
+ return { binDir, modelDir: join(cacheDir, 'asr', 'models'), exe: join(binDir, exe) };
110
+ }
111
+ /**
112
+ * 内置参数模板。用户不再手写 args:
113
+ * - whisper.cpp 系:`-m <model> -f <audio> -osrt -of <outbase> -l <lang> -t <threads>`
114
+ * - Python 封装:调用各自 CLI 的 srt 输出参数,音频作位置参数。
115
+ */
116
+ export function buildLocalArgs(engine, ctx) {
117
+ const lang = ctx.language ?? 'auto';
118
+ const threads = Math.max(1, Math.min(16, Math.trunc(ctx.threads) || 4));
119
+ const audio = ctx.audioToken ?? '{audio}';
120
+ const dir = ctx.dirToken ?? '{dir}';
121
+ const outBase = ctx.outBaseToken ?? '{outbase}';
122
+ switch (engine) {
123
+ case 'managed':
124
+ case 'whisper_cpp':
125
+ return [
126
+ '-m',
127
+ ctx.modelPath ?? '',
128
+ '-f',
129
+ audio,
130
+ '-osrt',
131
+ '-of',
132
+ outBase,
133
+ '-l',
134
+ lang,
135
+ '-t',
136
+ String(threads),
137
+ ];
138
+ case 'whisper_ctranslate2':
139
+ return [
140
+ '--model',
141
+ ctx.modelName,
142
+ '--language',
143
+ lang === 'auto' ? 'auto' : lang,
144
+ '--task',
145
+ 'transcribe',
146
+ '--output_format',
147
+ 'srt',
148
+ '--output_dir',
149
+ dir,
150
+ '--device',
151
+ 'cpu',
152
+ '--compute_type',
153
+ 'int8',
154
+ ];
155
+ case 'openai_whisper':
156
+ return [
157
+ '--model',
158
+ ctx.modelName,
159
+ '--language',
160
+ lang,
161
+ '--output_format',
162
+ 'srt',
163
+ '--output_dir',
164
+ dir,
165
+ ];
166
+ }
167
+ }
168
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/asr/catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 本地转写引擎探测。
3
+ *
4
+ * 目标:用户机器上「已经有什么」不该由用户自己填。探测顺序 =
5
+ * 显式配置 → 插件托管目录 → PATH 上的已知实现。
6
+ *
7
+ * 探测只做文件存在性判断,不 spawn:设置页可以高频调用。
8
+ */
9
+ import { type LocalEngineId, type ModelPresetId } from './catalog.js';
10
+ export interface LocalEngineSnapshot {
11
+ engine: LocalEngineId;
12
+ /** 可执行文件绝对路径(PATH 命中也为绝对路径)。 */
13
+ exe: string | null;
14
+ source: 'config' | 'managed' | 'path';
15
+ modelPath: string | null;
16
+ modelId: ModelPresetId;
17
+ /** exe 与(若需要)模型都就绪。 */
18
+ ready: boolean;
19
+ /** 未就绪时的人话原因。 */
20
+ reason: string | null;
21
+ }
22
+ export interface DetectInput {
23
+ cacheDir: string;
24
+ binPath?: string | undefined;
25
+ modelPath?: string | undefined;
26
+ modelId?: string | undefined;
27
+ }
28
+ /** 从可执行文件名反推引擎种类(用户显式填了路径的场景)。 */
29
+ export declare function inferEngine(exe: string): LocalEngineId;
30
+ /** PATH 上定位可执行文件(ffmpeg 等)。 */
31
+ export declare function locateOnPath(names: readonly string[]): string | null;
32
+ export declare function detectLocalEngine(input: DetectInput): LocalEngineSnapshot;
33
+ export declare function engineLabel(engine: LocalEngineId): string;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * 本地转写引擎探测。
3
+ *
4
+ * 目标:用户机器上「已经有什么」不该由用户自己填。探测顺序 =
5
+ * 显式配置 → 插件托管目录 → PATH 上的已知实现。
6
+ *
7
+ * 探测只做文件存在性判断,不 spawn:设置页可以高频调用。
8
+ */
9
+ import { existsSync, readdirSync, statSync } from 'node:fs';
10
+ import { basename, delimiter, isAbsolute, join } from 'node:path';
11
+ import { DEFAULT_MODEL_ID, LOCAL_ENGINES, managedPaths, modelPreset, } from './catalog.js';
12
+ const EXE = process.platform === 'win32' ? '.exe' : '';
13
+ function isFile(path) {
14
+ try {
15
+ return existsSync(path) && statSync(path).isFile();
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ /** 从可执行文件名反推引擎种类(用户显式填了路径的场景)。 */
22
+ export function inferEngine(exe) {
23
+ const base = basename(exe).toLowerCase().replace(/\.exe$/, '');
24
+ if (base.includes('ctranslate2') || base.includes('faster-whisper'))
25
+ return 'whisper_ctranslate2';
26
+ if (base.includes('whisper-cli') || base === 'main')
27
+ return 'whisper_cpp';
28
+ return 'openai_whisper';
29
+ }
30
+ function findOnPath(names) {
31
+ const dirs = (process.env.PATH ?? '').split(delimiter).filter((dir) => dir !== '');
32
+ for (const name of names) {
33
+ for (const dir of dirs) {
34
+ const candidate = join(dir, `${name}${EXE}`);
35
+ if (isFile(candidate))
36
+ return candidate;
37
+ }
38
+ }
39
+ return null;
40
+ }
41
+ /** PATH 上定位可执行文件(ffmpeg 等)。 */
42
+ export function locateOnPath(names) {
43
+ return findOnPath(names);
44
+ }
45
+ /** 托管模型目录里最新的 ggml 模型(用户可能下过别的型号)。 */
46
+ function pickManagedModel(modelDir) {
47
+ try {
48
+ const files = readdirSync(modelDir)
49
+ .filter((name) => name.startsWith('ggml-') && name.endsWith('.bin'))
50
+ .map((name) => join(modelDir, name))
51
+ .filter(isFile);
52
+ if (files.length === 0)
53
+ return null;
54
+ files.sort((a, b) => (statSync(b).mtimeMs ?? 0) - (statSync(a).mtimeMs ?? 0));
55
+ return files[0] ?? null;
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ }
61
+ export function detectLocalEngine(input) {
62
+ const modelId = (modelPreset(input.modelId).id ?? DEFAULT_MODEL_ID);
63
+ const managed = managedPaths(input.cacheDir);
64
+ const preset = modelPreset(modelId);
65
+ const managedModel = join(managed.modelDir, preset.file);
66
+ const resolveModel = (engine) => {
67
+ if (input.modelPath !== undefined && input.modelPath !== '' && isFile(input.modelPath)) {
68
+ return input.modelPath;
69
+ }
70
+ if (!LOCAL_ENGINES.find((entry) => entry.id === engine)?.needsModelFile)
71
+ return null;
72
+ if (isFile(managedModel))
73
+ return managedModel;
74
+ return pickManagedModel(managed.modelDir);
75
+ };
76
+ const finish = (engine, exe, source) => {
77
+ const needsModel = LOCAL_ENGINES.find((entry) => entry.id === engine)?.needsModelFile ?? false;
78
+ const modelPath = resolveModel(engine);
79
+ if (needsModel && modelPath === null) {
80
+ return {
81
+ engine,
82
+ exe,
83
+ source,
84
+ modelPath: null,
85
+ modelId,
86
+ ready: false,
87
+ reason: '找到转写程序,但还缺模型文件',
88
+ };
89
+ }
90
+ return { engine, exe, source, modelPath, modelId, ready: true, reason: null };
91
+ };
92
+ if (input.binPath !== undefined && input.binPath !== '' && isAbsolute(input.binPath) && isFile(input.binPath)) {
93
+ return finish(inferEngine(input.binPath), input.binPath, 'config');
94
+ }
95
+ if (isFile(managed.exe))
96
+ return finish('managed', managed.exe, 'managed');
97
+ for (const entry of LOCAL_ENGINES) {
98
+ if (entry.id === 'managed')
99
+ continue;
100
+ const found = findOnPath(entry.binNames);
101
+ if (found !== null)
102
+ return finish(entry.id, found, 'path');
103
+ }
104
+ return {
105
+ engine: 'managed',
106
+ exe: null,
107
+ source: 'managed',
108
+ modelPath: resolveModel('managed'),
109
+ modelId,
110
+ ready: false,
111
+ reason: '尚未安装本地转写引擎',
112
+ };
113
+ }
114
+ export function engineLabel(engine) {
115
+ return LOCAL_ENGINES.find((entry) => entry.id === engine)?.label ?? engine;
116
+ }
117
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/asr/detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 引擎选择:把「用户意图(engine/preset)」+「机器事实(探测结果/密钥)」
3
+ * 解析成一次转写要用的 AsrSettings。
4
+ *
5
+ * 默认 engine='auto' 的语义:
6
+ * 1. 用户显式配了旧版 provider → 尊重旧配置(0.2.x 行为不变);
7
+ * 2. 有云端密钥 → 云端(便宜、快、免下载);
8
+ * 3. 本机有可用本地引擎 → 本地;
9
+ * 4. 都没有 → null,并给出「为什么没有」的原因供结果提示与设置页展示。
10
+ */
11
+ import type { AsrSettings } from '../audio/types.js';
12
+ import type { LocalEngineSnapshot } from './detect.js';
13
+ export type AsrEngineMode = 'auto' | 'local' | 'cloud' | 'off' | 'legacy';
14
+ export type AsrUnavailableReason = 'engine_off' | 'cloud_missing_key' | 'cloud_missing_endpoint' | 'local_missing_engine' | 'local_missing_model';
15
+ export interface AsrRuntimeFacts {
16
+ detection: LocalEngineSnapshot;
17
+ /** 按云端预设 id 存放的密钥;null/缺省表示未配置。 */
18
+ keys: Readonly<Record<string, string | null>>;
19
+ /** 本地推理单请求超时(毫秒);CPU 上远大于云端。 */
20
+ localTimeoutMs: number;
21
+ /** 本地推理线程数;0 表示交给引擎决定(内部按 4 处理)。 */
22
+ localThreads?: number | undefined;
23
+ }
24
+ export interface AsrConfigSlice {
25
+ engine?: string | undefined;
26
+ preset?: string | undefined;
27
+ provider: string;
28
+ baseUrl?: string | undefined;
29
+ model?: string | undefined;
30
+ apiKey?: string | undefined;
31
+ language?: string | undefined;
32
+ args?: string[] | undefined;
33
+ timeoutMs: number;
34
+ maxConcurrency: number;
35
+ }
36
+ export interface AsrResolution {
37
+ settings: AsrSettings | null;
38
+ kind: 'cloud' | 'local' | 'legacy' | 'none';
39
+ reason: AsrUnavailableReason | null;
40
+ /** 面向用户的一句话解释(设置页与结果提示共用)。 */
41
+ detail: string | null;
42
+ }
43
+ export declare function resolveAsr(config: AsrConfigSlice, intentLanguage: string | undefined, env: NodeJS.ProcessEnv, facts: AsrRuntimeFacts | undefined): AsrResolution;
@@ -0,0 +1,143 @@
1
+ /**
2
+ * 引擎选择:把「用户意图(engine/preset)」+「机器事实(探测结果/密钥)」
3
+ * 解析成一次转写要用的 AsrSettings。
4
+ *
5
+ * 默认 engine='auto' 的语义:
6
+ * 1. 用户显式配了旧版 provider → 尊重旧配置(0.2.x 行为不变);
7
+ * 2. 有云端密钥 → 云端(便宜、快、免下载);
8
+ * 3. 本机有可用本地引擎 → 本地;
9
+ * 4. 都没有 → null,并给出「为什么没有」的原因供结果提示与设置页展示。
10
+ */
11
+ import { buildLocalArgs, cloudPreset, LOCAL_ENGINES, modelPreset } from './catalog.js';
12
+ function legacySettings(config, language, apiKey) {
13
+ return {
14
+ provider: config.provider,
15
+ baseUrl: config.baseUrl?.trim() || null,
16
+ model: config.model?.trim() || null,
17
+ apiKey,
18
+ language,
19
+ args: config.args !== undefined && config.args.length > 0 ? config.args : null,
20
+ timeoutMs: config.timeoutMs,
21
+ maxConcurrency: config.maxConcurrency,
22
+ };
23
+ }
24
+ function hasText(value) {
25
+ return typeof value === 'string' && value.trim().length > 0;
26
+ }
27
+ export function resolveAsr(config, intentLanguage, env, facts) {
28
+ const language = intentLanguage ?? config.language?.trim() ?? null;
29
+ const envKey = env.DSH_ASR_API_KEY?.trim();
30
+ // 没有运行时事实(单元测试 / 宿主未注入)时退回 0.2.x 语义。
31
+ if (facts === undefined) {
32
+ const provider = config.provider;
33
+ const needsKey = provider === 'openai_verbose' || provider === 'openai_basic';
34
+ const apiKey = hasText(envKey) ? envKey : config.apiKey?.trim() || null;
35
+ if (provider === 'none' || (needsKey && !hasText(apiKey))) {
36
+ return { settings: null, kind: 'none', reason: 'cloud_missing_key', detail: null };
37
+ }
38
+ return {
39
+ settings: legacySettings(config, language, apiKey),
40
+ kind: provider === 'local_cli' ? 'local' : 'cloud',
41
+ reason: null,
42
+ detail: null,
43
+ };
44
+ }
45
+ const mode = (config.engine ?? 'auto');
46
+ if (mode === 'off') {
47
+ return { settings: null, kind: 'none', reason: 'engine_off', detail: '转写已关闭' };
48
+ }
49
+ const preset = cloudPreset(config.preset ?? null);
50
+ const presetKey = facts.keys[preset.id] ?? null;
51
+ const cloudKey = hasText(envKey) ? envKey : presetKey;
52
+ const baseUrl = preset.editable ? config.baseUrl?.trim() ?? '' : preset.baseUrl;
53
+ const model = preset.editable ? config.model?.trim() ?? '' : preset.model;
54
+ const cloudReady = hasText(cloudKey) && hasText(baseUrl) && hasText(model);
55
+ const legacyExplicit = config.provider !== 'none' && mode !== 'cloud' && mode !== 'local';
56
+ if (mode === 'legacy' || legacyExplicit) {
57
+ const needsKey = config.provider === 'openai_verbose' || config.provider === 'openai_basic';
58
+ const apiKey = hasText(envKey) ? envKey : config.apiKey?.trim() || null;
59
+ if (config.provider === 'none') {
60
+ return { settings: null, kind: 'none', reason: 'engine_off', detail: '尚未选择转写引擎' };
61
+ }
62
+ if (needsKey && !hasText(apiKey)) {
63
+ return { settings: null, kind: 'none', reason: 'cloud_missing_key', detail: '缺少云端密钥' };
64
+ }
65
+ return {
66
+ settings: legacySettings(config, language, apiKey),
67
+ kind: config.provider === 'local_cli' ? 'local' : 'cloud',
68
+ reason: null,
69
+ detail: null,
70
+ };
71
+ }
72
+ const wantCloud = mode === 'cloud' || (mode === 'auto' && cloudReady);
73
+ if (wantCloud) {
74
+ if (!hasText(baseUrl)) {
75
+ return {
76
+ settings: null,
77
+ kind: 'none',
78
+ reason: 'cloud_missing_endpoint',
79
+ detail: '自定义端点需要填写 baseUrl',
80
+ };
81
+ }
82
+ if (!hasText(cloudKey)) {
83
+ return { settings: null, kind: 'none', reason: 'cloud_missing_key', detail: '尚未配置云端密钥' };
84
+ }
85
+ if (!hasText(model)) {
86
+ return { settings: null, kind: 'none', reason: 'cloud_missing_endpoint', detail: '尚未选择转写模型' };
87
+ }
88
+ return {
89
+ settings: {
90
+ provider: preset.protocol,
91
+ baseUrl: baseUrl.replace(/\/+$/, ''),
92
+ model,
93
+ apiKey: cloudKey,
94
+ language,
95
+ args: null,
96
+ timeoutMs: config.timeoutMs,
97
+ maxConcurrency: config.maxConcurrency,
98
+ },
99
+ kind: 'cloud',
100
+ reason: null,
101
+ detail: null,
102
+ };
103
+ }
104
+ const wantLocal = mode === 'local' || mode === 'auto';
105
+ if (wantLocal) {
106
+ const detection = facts.detection;
107
+ if (detection.exe === null || !detection.ready) {
108
+ return {
109
+ settings: null,
110
+ kind: 'none',
111
+ reason: detection.exe === null ? 'local_missing_engine' : 'local_missing_model',
112
+ detail: detection.reason ?? '本地转写尚未就绪',
113
+ };
114
+ }
115
+ const enginePreset = modelPreset(detection.modelId);
116
+ const needsModel = LOCAL_ENGINES.find((entry) => entry.id === detection.engine)?.needsModelFile ?? false;
117
+ if (needsModel && detection.modelPath === null) {
118
+ return { settings: null, kind: 'none', reason: 'local_missing_model', detail: '还缺模型文件' };
119
+ }
120
+ return {
121
+ settings: {
122
+ provider: 'local_cli',
123
+ baseUrl: null,
124
+ model: detection.exe,
125
+ apiKey: null,
126
+ language,
127
+ args: buildLocalArgs(detection.engine, {
128
+ modelPath: detection.modelPath,
129
+ modelName: detection.modelId === 'small-q5_1' ? 'small' : detection.modelId === 'base-q5_1' ? 'base' : 'medium',
130
+ language,
131
+ threads: facts.localThreads ?? 0,
132
+ }),
133
+ timeoutMs: facts.localTimeoutMs,
134
+ maxConcurrency: 1,
135
+ },
136
+ kind: 'local',
137
+ reason: null,
138
+ detail: needsModel ? `本地模型:${enginePreset.label}` : null,
139
+ };
140
+ }
141
+ return { settings: null, kind: 'none', reason: 'engine_off', detail: '尚未选择转写引擎' };
142
+ }
143
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/asr/engine.ts"],"names":[],"mappings":"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"}