@downcity/plugins 1.0.56 → 1.0.57
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/bin/BuiltinPlugins.d.ts.map +1 -1
- package/bin/BuiltinPlugins.js +0 -4
- package/bin/BuiltinPlugins.js.map +1 -1
- package/bin/asr/Plugin.d.ts +67 -7
- package/bin/asr/Plugin.d.ts.map +1 -1
- package/bin/asr/Plugin.js +229 -461
- package/bin/asr/Plugin.js.map +1 -1
- package/bin/asr/types/AsrPlugin.d.ts +114 -0
- package/bin/asr/types/AsrPlugin.d.ts.map +1 -0
- package/bin/asr/types/AsrPlugin.js +10 -0
- package/bin/asr/types/AsrPlugin.js.map +1 -0
- package/bin/image/ImagePlugin.d.ts +1 -1
- package/bin/image/ImagePlugin.d.ts.map +1 -1
- package/bin/image/ImagePlugin.js +23 -5
- package/bin/image/ImagePlugin.js.map +1 -1
- package/bin/index.d.ts +2 -0
- package/bin/index.d.ts.map +1 -1
- package/bin/tts/Plugin.d.ts +53 -6
- package/bin/tts/Plugin.d.ts.map +1 -1
- package/bin/tts/Plugin.js +197 -474
- package/bin/tts/Plugin.js.map +1 -1
- package/bin/tts/types/TtsPlugin.d.ts +63 -102
- package/bin/tts/types/TtsPlugin.d.ts.map +1 -1
- package/bin/tts/types/TtsPlugin.js +4 -3
- package/bin/tts/types/TtsPlugin.js.map +1 -1
- package/bin/web/PROMPT.d.ts +1 -1
- package/bin/web/PROMPT.d.ts.map +1 -1
- package/bin/web/PROMPT.js +1 -1
- package/bin/web/PROMPT.js.map +1 -1
- package/bin/web/Plugin.d.ts +66 -5
- package/bin/web/Plugin.d.ts.map +1 -1
- package/bin/web/Plugin.js +126 -450
- package/bin/web/Plugin.js.map +1 -1
- package/bin/web/WebPromptAssets.d.ts +1 -9
- package/bin/web/WebPromptAssets.d.ts.map +1 -1
- package/bin/web/WebPromptAssets.js +1 -11
- package/bin/web/WebPromptAssets.js.map +1 -1
- package/bin/web/runtime/Install.d.ts +19 -0
- package/bin/web/runtime/Install.d.ts.map +1 -0
- package/bin/web/runtime/Install.js +178 -0
- package/bin/web/runtime/Install.js.map +1 -0
- package/bin/web/types/WebPlugin.d.ts +37 -109
- package/bin/web/types/WebPlugin.d.ts.map +1 -1
- package/bin/web/types/WebPlugin.js +5 -7
- package/bin/web/types/WebPlugin.js.map +1 -1
- package/package.json +2 -2
- package/src/BuiltinPlugins.ts +0 -4
- package/src/asr/Plugin.ts +264 -483
- package/src/asr/types/AsrPlugin.ts +118 -0
- package/src/image/ImagePlugin.ts +23 -5
- package/src/index.ts +12 -0
- package/src/tts/Plugin.ts +225 -492
- package/src/tts/types/TtsPlugin.ts +67 -102
- package/src/web/PROMPT.ts +1 -1
- package/src/web/PROMPT.ts.txt +32 -6
- package/src/web/Plugin.ts +119 -453
- package/src/web/WebPromptAssets.ts +1 -13
- package/src/web/runtime/Install.ts +241 -0
- package/src/web/types/WebPlugin.ts +37 -113
- package/bin/asr/Config.d.ts +0 -43
- package/bin/asr/Config.d.ts.map +0 -1
- package/bin/asr/Config.js +0 -107
- package/bin/asr/Config.js.map +0 -1
- package/bin/asr/Dependency.d.ts +0 -77
- package/bin/asr/Dependency.d.ts.map +0 -1
- package/bin/asr/Dependency.js +0 -238
- package/bin/asr/Dependency.js.map +0 -1
- package/bin/asr/InboundAugment.d.ts +0 -17
- package/bin/asr/InboundAugment.d.ts.map +0 -1
- package/bin/asr/InboundAugment.js +0 -47
- package/bin/asr/InboundAugment.js.map +0 -1
- package/bin/asr/ModelCatalog.d.ts +0 -29
- package/bin/asr/ModelCatalog.d.ts.map +0 -1
- package/bin/asr/ModelCatalog.js +0 -25
- package/bin/asr/ModelCatalog.js.map +0 -1
- package/bin/tts/Dependency.d.ts +0 -90
- package/bin/tts/Dependency.d.ts.map +0 -1
- package/bin/tts/Dependency.js +0 -344
- package/bin/tts/Dependency.js.map +0 -1
- package/bin/tts/PluginSupport.d.ts +0 -25
- package/bin/tts/PluginSupport.d.ts.map +0 -1
- package/bin/tts/PluginSupport.js +0 -72
- package/bin/tts/PluginSupport.js.map +0 -1
- package/bin/tts/runtime/Catalog.d.ts +0 -21
- package/bin/tts/runtime/Catalog.d.ts.map +0 -1
- package/bin/tts/runtime/Catalog.js +0 -90
- package/bin/tts/runtime/Catalog.js.map +0 -1
- package/bin/tts/runtime/DependencyInstaller.d.ts +0 -143
- package/bin/tts/runtime/DependencyInstaller.d.ts.map +0 -1
- package/bin/tts/runtime/DependencyInstaller.js +0 -261
- package/bin/tts/runtime/DependencyInstaller.js.map +0 -1
- package/bin/tts/runtime/Installer.d.ts +0 -89
- package/bin/tts/runtime/Installer.d.ts.map +0 -1
- package/bin/tts/runtime/Installer.js +0 -188
- package/bin/tts/runtime/Installer.js.map +0 -1
- package/bin/tts/runtime/Paths.d.ts +0 -20
- package/bin/tts/runtime/Paths.d.ts.map +0 -1
- package/bin/tts/runtime/Paths.js +0 -32
- package/bin/tts/runtime/Paths.js.map +0 -1
- package/bin/tts/runtime/Synthesizer.d.ts +0 -44
- package/bin/tts/runtime/Synthesizer.d.ts.map +0 -1
- package/bin/tts/runtime/Synthesizer.js +0 -363
- package/bin/tts/runtime/Synthesizer.js.map +0 -1
- package/bin/tts/types/Tts.d.ts +0 -91
- package/bin/tts/types/Tts.d.ts.map +0 -1
- package/bin/tts/types/Tts.js +0 -9
- package/bin/tts/types/Tts.js.map +0 -1
- package/bin/voice/Config.d.ts +0 -43
- package/bin/voice/Config.d.ts.map +0 -1
- package/bin/voice/Config.js +0 -104
- package/bin/voice/Config.js.map +0 -1
- package/bin/voice/Dependency.d.ts +0 -77
- package/bin/voice/Dependency.d.ts.map +0 -1
- package/bin/voice/Dependency.js +0 -237
- package/bin/voice/Dependency.js.map +0 -1
- package/bin/voice/InboundAugment.d.ts +0 -17
- package/bin/voice/InboundAugment.d.ts.map +0 -1
- package/bin/voice/InboundAugment.js +0 -47
- package/bin/voice/InboundAugment.js.map +0 -1
- package/bin/voice/ModelCatalog.d.ts +0 -29
- package/bin/voice/ModelCatalog.d.ts.map +0 -1
- package/bin/voice/ModelCatalog.js +0 -25
- package/bin/voice/ModelCatalog.js.map +0 -1
- package/bin/voice/runtime/Catalog.d.ts +0 -18
- package/bin/voice/runtime/Catalog.d.ts.map +0 -1
- package/bin/voice/runtime/Catalog.js +0 -61
- package/bin/voice/runtime/Catalog.js.map +0 -1
- package/bin/voice/runtime/DependencyInstaller.d.ts +0 -145
- package/bin/voice/runtime/DependencyInstaller.d.ts.map +0 -1
- package/bin/voice/runtime/DependencyInstaller.js +0 -309
- package/bin/voice/runtime/DependencyInstaller.js.map +0 -1
- package/bin/voice/runtime/Installer.d.ts +0 -94
- package/bin/voice/runtime/Installer.d.ts.map +0 -1
- package/bin/voice/runtime/Installer.js +0 -200
- package/bin/voice/runtime/Installer.js.map +0 -1
- package/bin/voice/runtime/Paths.d.ts +0 -8
- package/bin/voice/runtime/Paths.d.ts.map +0 -1
- package/bin/voice/runtime/Paths.js +0 -26
- package/bin/voice/runtime/Paths.js.map +0 -1
- package/bin/voice/runtime/Transcriber.d.ts +0 -57
- package/bin/voice/runtime/Transcriber.d.ts.map +0 -1
- package/bin/voice/runtime/Transcriber.js +0 -329
- package/bin/voice/runtime/Transcriber.js.map +0 -1
- package/bin/voice/types/Voice.d.ts +0 -58
- package/bin/voice/types/Voice.d.ts.map +0 -1
- package/bin/voice/types/Voice.js +0 -9
- package/bin/voice/types/Voice.js.map +0 -1
- package/bin/voice/types/VoicePlugin.d.ts +0 -190
- package/bin/voice/types/VoicePlugin.d.ts.map +0 -1
- package/bin/voice/types/VoicePlugin.js +0 -9
- package/bin/voice/types/VoicePlugin.js.map +0 -1
- package/bin/web/Dependency.d.ts +0 -10
- package/bin/web/Dependency.d.ts.map +0 -1
- package/bin/web/Dependency.js +0 -10
- package/bin/web/Dependency.js.map +0 -1
- package/bin/web/PROMPT.agent-browser.d.ts +0 -7
- package/bin/web/PROMPT.agent-browser.d.ts.map +0 -1
- package/bin/web/PROMPT.agent-browser.js +0 -8
- package/bin/web/PROMPT.agent-browser.js.map +0 -1
- package/bin/web/PROMPT.web-access.d.ts +0 -7
- package/bin/web/PROMPT.web-access.d.ts.map +0 -1
- package/bin/web/PROMPT.web-access.js +0 -8
- package/bin/web/PROMPT.web-access.js.map +0 -1
- package/bin/web/runtime/Config.d.ts +0 -21
- package/bin/web/runtime/Config.d.ts.map +0 -1
- package/bin/web/runtime/Config.js +0 -79
- package/bin/web/runtime/Config.js.map +0 -1
- package/bin/web/runtime/Source.d.ts +0 -29
- package/bin/web/runtime/Source.d.ts.map +0 -1
- package/bin/web/runtime/Source.js +0 -209
- package/bin/web/runtime/Source.js.map +0 -1
- package/src/asr/Config.ts +0 -138
- package/src/asr/Dependency.ts +0 -336
- package/src/asr/InboundAugment.ts +0 -59
- package/src/asr/ModelCatalog.ts +0 -43
- package/src/tts/Dependency.ts +0 -473
- package/src/tts/PluginSupport.ts +0 -85
- package/src/tts/runtime/Catalog.ts +0 -97
- package/src/tts/runtime/DependencyInstaller.ts +0 -436
- package/src/tts/runtime/Installer.ts +0 -297
- package/src/tts/runtime/Paths.ts +0 -39
- package/src/tts/runtime/Synthesizer.ts +0 -480
- package/src/tts/types/Tts.ts +0 -99
- package/src/voice/Config.ts +0 -135
- package/src/voice/Dependency.ts +0 -329
- package/src/voice/InboundAugment.ts +0 -59
- package/src/voice/ModelCatalog.ts +0 -43
- package/src/voice/runtime/Catalog.ts +0 -68
- package/src/voice/runtime/DependencyInstaller.ts +0 -505
- package/src/voice/runtime/Installer.ts +0 -324
- package/src/voice/runtime/Paths.ts +0 -26
- package/src/voice/runtime/Transcriber.ts +0 -467
- package/src/voice/types/Voice.ts +0 -68
- package/src/voice/types/VoicePlugin.ts +0 -194
- package/src/web/Dependency.ts +0 -17
- package/src/web/PROMPT.agent-browser.ts +0 -9
- package/src/web/PROMPT.agent-browser.ts.txt +0 -17
- package/src/web/PROMPT.web-access.ts +0 -9
- package/src/web/PROMPT.web-access.ts.txt +0 -13
- package/src/web/runtime/Config.ts +0 -105
- package/src/web/runtime/Source.ts +0 -257
package/src/tts/Dependency.ts
DELETED
|
@@ -1,473 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TTS Plugin Dependency Helper。
|
|
3
|
-
*
|
|
4
|
-
* 关键点(中文)
|
|
5
|
-
* - TTS 的模型安装、配置读写与可用性检查都内聚在 plugin 内部。
|
|
6
|
-
* - Console 只调用 plugin action,不直接理解底层资源结构。
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { execFile as execFileCb } from "node:child_process";
|
|
10
|
-
import { promisify } from "node:util";
|
|
11
|
-
import type { JsonObject, JsonValue } from "@downcity/agent/internal/types/common/Json.js";
|
|
12
|
-
import type { PluginCommandContext } from "@downcity/agent/internal/plugin/types/Plugin.js";
|
|
13
|
-
import type {
|
|
14
|
-
TtsInstallInput,
|
|
15
|
-
TtsPluginConfig,
|
|
16
|
-
} from "@/tts/types/TtsPlugin.js";
|
|
17
|
-
import type { TtsModelId } from "@/tts/types/Tts.js";
|
|
18
|
-
import {
|
|
19
|
-
getTtsModelCatalogItem,
|
|
20
|
-
resolveTtsModelId,
|
|
21
|
-
TTS_MODEL_CATALOG,
|
|
22
|
-
} from "@/tts/runtime/Catalog.js";
|
|
23
|
-
import {
|
|
24
|
-
detectLocalTtsModelInstallState,
|
|
25
|
-
installTtsModelFromHuggingFace,
|
|
26
|
-
} from "@/tts/runtime/Installer.js";
|
|
27
|
-
import {
|
|
28
|
-
ensureTtsVirtualEnv,
|
|
29
|
-
installTtsDependencies,
|
|
30
|
-
resolveDefaultTtsVenvPythonBin,
|
|
31
|
-
resolveTtsRunnersByModels,
|
|
32
|
-
} from "@/tts/runtime/DependencyInstaller.js";
|
|
33
|
-
import { resolveTtsModelsRootDir } from "@/tts/runtime/Paths.js";
|
|
34
|
-
|
|
35
|
-
const execFileAsync = promisify(execFileCb);
|
|
36
|
-
|
|
37
|
-
type PythonVersionInfo = {
|
|
38
|
-
major: number;
|
|
39
|
-
minor: number;
|
|
40
|
-
patch: number;
|
|
41
|
-
raw: string;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export interface TtsDependencyCheckResult {
|
|
45
|
-
/**
|
|
46
|
-
* 当前依赖是否可用。
|
|
47
|
-
*/
|
|
48
|
-
available: boolean;
|
|
49
|
-
/**
|
|
50
|
-
* 不可用原因列表。
|
|
51
|
-
*/
|
|
52
|
-
reasons: string[];
|
|
53
|
-
/**
|
|
54
|
-
* 结构化附加数据(可选)。
|
|
55
|
-
*/
|
|
56
|
-
details?: JsonValue;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface TtsDependencyInstallResult {
|
|
60
|
-
/**
|
|
61
|
-
* 安装是否成功。
|
|
62
|
-
*/
|
|
63
|
-
success: boolean;
|
|
64
|
-
/**
|
|
65
|
-
* 人类可读消息(可选)。
|
|
66
|
-
*/
|
|
67
|
-
message?: string;
|
|
68
|
-
/**
|
|
69
|
-
* 结构化附加数据(可选)。
|
|
70
|
-
*/
|
|
71
|
-
details?: JsonValue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function readTtsPluginRecord(context: PluginCommandContext): Record<string, unknown> {
|
|
75
|
-
const current = context.config.plugins?.tts;
|
|
76
|
-
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
77
|
-
return {};
|
|
78
|
-
}
|
|
79
|
-
return current as Record<string, unknown>;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function toJsonObject(input: Record<string, unknown> | null | undefined): JsonObject {
|
|
83
|
-
const out: JsonObject = {};
|
|
84
|
-
if (!input) return out;
|
|
85
|
-
for (const [key, value] of Object.entries(input)) {
|
|
86
|
-
if (value === undefined) continue;
|
|
87
|
-
if (
|
|
88
|
-
value === null ||
|
|
89
|
-
typeof value === "string" ||
|
|
90
|
-
typeof value === "number" ||
|
|
91
|
-
typeof value === "boolean"
|
|
92
|
-
) {
|
|
93
|
-
out[key] = value;
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
if (Array.isArray(value)) {
|
|
97
|
-
out[key] = value
|
|
98
|
-
.filter((item) => item !== undefined)
|
|
99
|
-
.map((item) => item as JsonValue);
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
if (typeof value === "object") {
|
|
103
|
-
out[key] = toJsonObject(value as Record<string, unknown>);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return out;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function normalizeFormat(value: unknown): "wav" | "flac" {
|
|
110
|
-
return String(value || "").trim().toLowerCase() === "flac" ? "flac" : "wav";
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function normalizeTtsPythonBin(value: unknown): string {
|
|
114
|
-
const text = String(value || "").trim();
|
|
115
|
-
if (!text || text === "python3") {
|
|
116
|
-
return resolveDefaultTtsVenvPythonBin();
|
|
117
|
-
}
|
|
118
|
-
return text;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function readPythonVersionInfo(pythonBin: string): Promise<PythonVersionInfo | null> {
|
|
122
|
-
try {
|
|
123
|
-
const { stdout, stderr } = await execFileAsync(pythonBin, ["--version"], {
|
|
124
|
-
timeout: 15_000,
|
|
125
|
-
maxBuffer: 1024 * 1024,
|
|
126
|
-
});
|
|
127
|
-
const text = String(stdout || stderr || "").trim();
|
|
128
|
-
const match = text.match(/Python\s+(\d+)\.(\d+)\.(\d+)/i);
|
|
129
|
-
if (!match) return null;
|
|
130
|
-
return {
|
|
131
|
-
major: Number(match[1]),
|
|
132
|
-
minor: Number(match[2]),
|
|
133
|
-
patch: Number(match[3]),
|
|
134
|
-
raw: text,
|
|
135
|
-
};
|
|
136
|
-
} catch {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function getTtsPythonCompatibilityReason(params: {
|
|
142
|
-
modelId: TtsModelId;
|
|
143
|
-
version: PythonVersionInfo | null;
|
|
144
|
-
}): string | null {
|
|
145
|
-
if (!params.version) return null;
|
|
146
|
-
if (params.modelId === "kokoro-82m") {
|
|
147
|
-
if (params.version.major > 3 || (params.version.major === 3 && params.version.minor >= 13)) {
|
|
148
|
-
return `kokoro-82m currently requires Python < 3.13, current is ${params.version.major}.${params.version.minor}.${params.version.patch}`;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* 读取 TTS Plugin 配置。
|
|
156
|
-
*/
|
|
157
|
-
export function readTtsPluginConfig(context: PluginCommandContext): TtsPluginConfig {
|
|
158
|
-
const { enabled: _ignoredEnabled, ...current } = readTtsPluginRecord(context) as Record<
|
|
159
|
-
string,
|
|
160
|
-
unknown
|
|
161
|
-
> & {
|
|
162
|
-
enabled?: boolean;
|
|
163
|
-
};
|
|
164
|
-
return {
|
|
165
|
-
provider: "local",
|
|
166
|
-
...(typeof current.modelId === "string" ? { modelId: current.modelId } : {}),
|
|
167
|
-
...(typeof current.modelsDir === "string" ? { modelsDir: current.modelsDir } : {}),
|
|
168
|
-
pythonBin: normalizeTtsPythonBin(current.pythonBin),
|
|
169
|
-
...(typeof current.language === "string" ? { language: current.language } : {}),
|
|
170
|
-
...(typeof current.voice === "string" ? { voice: current.voice } : {}),
|
|
171
|
-
...(typeof current.outputDir === "string" ? { outputDir: current.outputDir } : {}),
|
|
172
|
-
...(typeof current.timeoutMs === "number" ? { timeoutMs: current.timeoutMs } : {}),
|
|
173
|
-
...(typeof current.speed === "number" ? { speed: current.speed } : {}),
|
|
174
|
-
format: normalizeFormat(current.format),
|
|
175
|
-
...(Array.isArray(current.installedModels)
|
|
176
|
-
? { installedModels: current.installedModels }
|
|
177
|
-
: {}),
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* 写入完整 TTS plugin 配置。
|
|
183
|
-
*/
|
|
184
|
-
export async function writeTtsPluginConfig(params: {
|
|
185
|
-
/**
|
|
186
|
-
* 当前执行上下文。
|
|
187
|
-
*/
|
|
188
|
-
context: PluginCommandContext;
|
|
189
|
-
/**
|
|
190
|
-
* 目标配置值。
|
|
191
|
-
*/
|
|
192
|
-
value: TtsPluginConfig;
|
|
193
|
-
}): Promise<TtsPluginConfig> {
|
|
194
|
-
if (!params.context.config.plugins) {
|
|
195
|
-
params.context.config.plugins = {};
|
|
196
|
-
}
|
|
197
|
-
const next: Record<string, unknown> = {
|
|
198
|
-
...(params.value as Record<string, unknown>),
|
|
199
|
-
};
|
|
200
|
-
delete next.enabled;
|
|
201
|
-
params.context.config.plugins.tts = toJsonObject(next);
|
|
202
|
-
await params.context.pluginConfig.persistProjectPlugins(params.context.config.plugins);
|
|
203
|
-
return params.context.config.plugins.tts as TtsPluginConfig;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async function checkPythonPackageImports(params: {
|
|
207
|
-
pythonBin: string;
|
|
208
|
-
modelId: TtsModelId;
|
|
209
|
-
}): Promise<string | null> {
|
|
210
|
-
const importScript =
|
|
211
|
-
params.modelId === "qwen3-tts-0.6b"
|
|
212
|
-
? "import qwen_tts, soundfile"
|
|
213
|
-
: "import kokoro, soundfile";
|
|
214
|
-
try {
|
|
215
|
-
await execFileAsync(params.pythonBin, ["-c", importScript], {
|
|
216
|
-
timeout: 30_000,
|
|
217
|
-
maxBuffer: 1024 * 1024,
|
|
218
|
-
});
|
|
219
|
-
return null;
|
|
220
|
-
} catch (error) {
|
|
221
|
-
return `python packages missing: ${String(error)}`;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* 检查 TTS 依赖可用性。
|
|
227
|
-
*/
|
|
228
|
-
export async function checkTtsSynthesizer(
|
|
229
|
-
context: PluginCommandContext,
|
|
230
|
-
): Promise<TtsDependencyCheckResult> {
|
|
231
|
-
const config = readTtsPluginConfig(context);
|
|
232
|
-
const reasons: string[] = [];
|
|
233
|
-
const modelId = resolveTtsModelId(String(config.modelId || ""));
|
|
234
|
-
if (!modelId) {
|
|
235
|
-
reasons.push("tts modelId is missing");
|
|
236
|
-
return {
|
|
237
|
-
available: false,
|
|
238
|
-
reasons,
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const modelsRootDir = resolveTtsModelsRootDir({
|
|
243
|
-
projectRoot: context.rootPath,
|
|
244
|
-
modelsDir: config.modelsDir,
|
|
245
|
-
});
|
|
246
|
-
const installState = await detectLocalTtsModelInstallState({
|
|
247
|
-
modelId,
|
|
248
|
-
modelsRootDir,
|
|
249
|
-
});
|
|
250
|
-
if (!installState.installed) {
|
|
251
|
-
reasons.push(`tts model is not installed: ${modelId}`);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const pythonBin = normalizeTtsPythonBin(config.pythonBin);
|
|
255
|
-
const pythonVersion = await readPythonVersionInfo(pythonBin);
|
|
256
|
-
if (!pythonVersion) {
|
|
257
|
-
reasons.push(`python runtime missing: ${pythonBin}`);
|
|
258
|
-
return {
|
|
259
|
-
available: false,
|
|
260
|
-
reasons,
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const compatibilityReason = getTtsPythonCompatibilityReason({
|
|
265
|
-
modelId,
|
|
266
|
-
version: pythonVersion,
|
|
267
|
-
});
|
|
268
|
-
if (compatibilityReason) {
|
|
269
|
-
reasons.push(compatibilityReason);
|
|
270
|
-
return {
|
|
271
|
-
available: false,
|
|
272
|
-
reasons,
|
|
273
|
-
details: {
|
|
274
|
-
modelDir: installState.modelDir,
|
|
275
|
-
source: installState.source || null,
|
|
276
|
-
pythonVersion: pythonVersion.raw,
|
|
277
|
-
},
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const importError = await checkPythonPackageImports({
|
|
282
|
-
pythonBin,
|
|
283
|
-
modelId,
|
|
284
|
-
});
|
|
285
|
-
if (importError) {
|
|
286
|
-
reasons.push(importError);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return {
|
|
290
|
-
available: reasons.length === 0,
|
|
291
|
-
reasons,
|
|
292
|
-
details: {
|
|
293
|
-
modelDir: installState.modelDir,
|
|
294
|
-
source: installState.source || null,
|
|
295
|
-
},
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* 安装或修复 TTS 依赖。
|
|
301
|
-
*/
|
|
302
|
-
export async function installTtsSynthesizer(params: {
|
|
303
|
-
/**
|
|
304
|
-
* 当前执行上下文。
|
|
305
|
-
*/
|
|
306
|
-
context: PluginCommandContext;
|
|
307
|
-
/**
|
|
308
|
-
* 安装输入(可选)。
|
|
309
|
-
*/
|
|
310
|
-
input?: TtsInstallInput;
|
|
311
|
-
}): Promise<TtsDependencyInstallResult> {
|
|
312
|
-
const context = params.context;
|
|
313
|
-
const input = params.input;
|
|
314
|
-
const config = readTtsPluginConfig(context);
|
|
315
|
-
const logs: string[] = [];
|
|
316
|
-
|
|
317
|
-
const requestedModelIds =
|
|
318
|
-
(Array.isArray(input?.modelIds) ? input.modelIds : [])
|
|
319
|
-
.map((item) => resolveTtsModelId(String(item || "").trim()))
|
|
320
|
-
.filter((item): item is TtsModelId => Boolean(item));
|
|
321
|
-
const selectedModelIds: TtsModelId[] =
|
|
322
|
-
requestedModelIds.length > 0
|
|
323
|
-
? requestedModelIds
|
|
324
|
-
: [resolveTtsModelId(String(config.modelId || "qwen3-tts-0.6b")) || "qwen3-tts-0.6b"];
|
|
325
|
-
|
|
326
|
-
const modelsRootDir = resolveTtsModelsRootDir({
|
|
327
|
-
projectRoot: context.rootPath,
|
|
328
|
-
modelsDir: input?.modelsDir || config.modelsDir,
|
|
329
|
-
});
|
|
330
|
-
logs.push(`models dir: ${modelsRootDir}`);
|
|
331
|
-
|
|
332
|
-
const installResults: JsonObject[] = [];
|
|
333
|
-
for (const modelId of selectedModelIds) {
|
|
334
|
-
logs.push(`prepare model: ${modelId}`);
|
|
335
|
-
const model = getTtsModelCatalogItem(modelId);
|
|
336
|
-
if (!model) {
|
|
337
|
-
throw new Error(`Unsupported tts model: ${modelId}`);
|
|
338
|
-
}
|
|
339
|
-
const installState = await detectLocalTtsModelInstallState({
|
|
340
|
-
modelId,
|
|
341
|
-
modelsRootDir,
|
|
342
|
-
});
|
|
343
|
-
if (!installState.installed || input?.force === true) {
|
|
344
|
-
logs.push(
|
|
345
|
-
installState.installed && input?.force === true
|
|
346
|
-
? `redownload model: ${modelId}`
|
|
347
|
-
: `download model: ${modelId}`,
|
|
348
|
-
);
|
|
349
|
-
const result = await installTtsModelFromHuggingFace({
|
|
350
|
-
model,
|
|
351
|
-
modelsRootDir,
|
|
352
|
-
force: input?.force === true,
|
|
353
|
-
hfToken: input?.hfToken,
|
|
354
|
-
});
|
|
355
|
-
logs.push(
|
|
356
|
-
`model ready: ${modelId} (downloaded ${result.downloadedFiles}, skipped ${result.skippedFiles})`,
|
|
357
|
-
);
|
|
358
|
-
installResults.push(toJsonObject({
|
|
359
|
-
modelId,
|
|
360
|
-
downloadedFiles: result.downloadedFiles,
|
|
361
|
-
skippedFiles: result.skippedFiles,
|
|
362
|
-
}));
|
|
363
|
-
continue;
|
|
364
|
-
}
|
|
365
|
-
logs.push(`reuse installed model: ${modelId}`);
|
|
366
|
-
installResults.push(toJsonObject({
|
|
367
|
-
modelId,
|
|
368
|
-
downloadedFiles: 0,
|
|
369
|
-
skippedFiles: 0,
|
|
370
|
-
reused: true,
|
|
371
|
-
}));
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const basePythonBin = String(input?.pythonBin || "").trim() || "python3";
|
|
375
|
-
const basePythonVersion = await readPythonVersionInfo(basePythonBin);
|
|
376
|
-
let resolvedPythonBin = normalizeTtsPythonBin(config.pythonBin);
|
|
377
|
-
let dependencyDetails: JsonValue | undefined;
|
|
378
|
-
const compatibilityFailures = selectedModelIds
|
|
379
|
-
.map((modelId) => getTtsPythonCompatibilityReason({
|
|
380
|
-
modelId,
|
|
381
|
-
version: basePythonVersion,
|
|
382
|
-
}))
|
|
383
|
-
.filter((item): item is string => Boolean(item));
|
|
384
|
-
if (compatibilityFailures.length > 0) {
|
|
385
|
-
logs.push(...compatibilityFailures);
|
|
386
|
-
return {
|
|
387
|
-
success: false,
|
|
388
|
-
message: compatibilityFailures.join(" · "),
|
|
389
|
-
details: {
|
|
390
|
-
pythonVersion: basePythonVersion?.raw || basePythonBin,
|
|
391
|
-
logs,
|
|
392
|
-
},
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
if (input?.installDeps !== false) {
|
|
396
|
-
logs.push(`prepare python env from: ${basePythonBin}`);
|
|
397
|
-
const dependencyResult = await installTtsDependencies({
|
|
398
|
-
pythonBin: basePythonBin,
|
|
399
|
-
runners: resolveTtsRunnersByModels(selectedModelIds),
|
|
400
|
-
});
|
|
401
|
-
resolvedPythonBin = dependencyResult.pythonBin;
|
|
402
|
-
logs.push(`python env ready: ${dependencyResult.pythonBin}`);
|
|
403
|
-
for (const item of dependencyResult.items) {
|
|
404
|
-
logs.push(
|
|
405
|
-
item.skipped
|
|
406
|
-
? `dependency ${item.runner}: already installed`
|
|
407
|
-
: `dependency ${item.runner}: installed`,
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
dependencyDetails = toJsonObject({
|
|
411
|
-
pythonBin: dependencyResult.pythonBin,
|
|
412
|
-
runners: dependencyResult.runners,
|
|
413
|
-
usedVirtualEnv: dependencyResult.usedVirtualEnv,
|
|
414
|
-
venvDir: dependencyResult.venvDir,
|
|
415
|
-
});
|
|
416
|
-
} else {
|
|
417
|
-
logs.push(`prepare python env only: ${basePythonBin}`);
|
|
418
|
-
resolvedPythonBin = await ensureTtsVirtualEnv({
|
|
419
|
-
basePythonBin,
|
|
420
|
-
});
|
|
421
|
-
logs.push(`python env ready: ${resolvedPythonBin}`);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
const activeModelId =
|
|
425
|
-
resolveTtsModelId(String(input?.activeModel || "")) || selectedModelIds[0];
|
|
426
|
-
const nextConfig = await writeTtsPluginConfig({
|
|
427
|
-
context,
|
|
428
|
-
value: {
|
|
429
|
-
...config,
|
|
430
|
-
provider: "local",
|
|
431
|
-
modelId: activeModelId,
|
|
432
|
-
modelsDir: modelsRootDir,
|
|
433
|
-
pythonBin: resolvedPythonBin,
|
|
434
|
-
format: normalizeFormat(input?.format || config.format),
|
|
435
|
-
installedModels: selectedModelIds,
|
|
436
|
-
},
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
return {
|
|
440
|
-
success: true,
|
|
441
|
-
message: "tts synthesizer installed",
|
|
442
|
-
details: {
|
|
443
|
-
plugin: toJsonObject(nextConfig as Record<string, unknown>),
|
|
444
|
-
models: installResults,
|
|
445
|
-
dependency: dependencyDetails || null,
|
|
446
|
-
logs,
|
|
447
|
-
},
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* 列出可用于 TTS 的内置模型选项。
|
|
453
|
-
*/
|
|
454
|
-
export function listTtsModelOptions(): Array<{
|
|
455
|
-
/**
|
|
456
|
-
* 选项标签。
|
|
457
|
-
*/
|
|
458
|
-
label: string;
|
|
459
|
-
/**
|
|
460
|
-
* 选项值。
|
|
461
|
-
*/
|
|
462
|
-
value: string;
|
|
463
|
-
/**
|
|
464
|
-
* 选项说明。
|
|
465
|
-
*/
|
|
466
|
-
hint: string;
|
|
467
|
-
}> {
|
|
468
|
-
return TTS_MODEL_CATALOG.map((item) => ({
|
|
469
|
-
label: item.label,
|
|
470
|
-
value: item.id,
|
|
471
|
-
hint: item.description,
|
|
472
|
-
}));
|
|
473
|
-
}
|
package/src/tts/PluginSupport.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TTS plugin 输入与 JSON 辅助。
|
|
3
|
-
*
|
|
4
|
-
* 关键点(中文)
|
|
5
|
-
* - 只放 plugin action 映射中的通用 option 读取与 JSON 标准化。
|
|
6
|
-
* - TTS 依赖安装、合成执行等领域逻辑仍留在 dependency/runtime 模块。
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { JsonObject, JsonValue } from "@downcity/agent/internal/types/common/Json.js";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* 将普通对象转为可持久化 JSON object。
|
|
13
|
-
*/
|
|
14
|
-
export function toJsonObject(
|
|
15
|
-
input: Record<string, unknown> | null | undefined,
|
|
16
|
-
): JsonObject | null {
|
|
17
|
-
if (!input) return null;
|
|
18
|
-
const out: JsonObject = {};
|
|
19
|
-
for (const [key, value] of Object.entries(input)) {
|
|
20
|
-
if (value === undefined) continue;
|
|
21
|
-
if (
|
|
22
|
-
value === null ||
|
|
23
|
-
typeof value === "string" ||
|
|
24
|
-
typeof value === "number" ||
|
|
25
|
-
typeof value === "boolean"
|
|
26
|
-
) {
|
|
27
|
-
out[key] = value;
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (Array.isArray(value)) {
|
|
31
|
-
out[key] = value
|
|
32
|
-
.filter((item) => item !== undefined)
|
|
33
|
-
.map((item) => item as JsonValue);
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
if (typeof value === "object") {
|
|
37
|
-
out[key] = toJsonObject(value as Record<string, unknown>) || {};
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return out;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 读取字符串选项。
|
|
45
|
-
*/
|
|
46
|
-
export function getStringOpt(
|
|
47
|
-
opts: Record<string, JsonValue>,
|
|
48
|
-
key: string,
|
|
49
|
-
): string | undefined {
|
|
50
|
-
const value = opts[key];
|
|
51
|
-
if (typeof value !== "string") return undefined;
|
|
52
|
-
const trimmed = value.trim();
|
|
53
|
-
return trimmed || undefined;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* 读取布尔选项。
|
|
58
|
-
*/
|
|
59
|
-
export function getBooleanOpt(
|
|
60
|
-
opts: Record<string, JsonValue>,
|
|
61
|
-
key: string,
|
|
62
|
-
defaultValue: boolean,
|
|
63
|
-
): boolean {
|
|
64
|
-
const value = opts[key];
|
|
65
|
-
if (typeof value === "boolean") return value;
|
|
66
|
-
if (typeof value === "string") {
|
|
67
|
-
const normalized = value.trim().toLowerCase();
|
|
68
|
-
if (["1", "true", "yes", "y", "on"].includes(normalized)) return true;
|
|
69
|
-
if (["0", "false", "no", "n", "off"].includes(normalized)) return false;
|
|
70
|
-
}
|
|
71
|
-
return defaultValue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* 读取数字选项。
|
|
76
|
-
*/
|
|
77
|
-
export function getNumberOpt(
|
|
78
|
-
opts: Record<string, JsonValue>,
|
|
79
|
-
key: string,
|
|
80
|
-
): number | undefined {
|
|
81
|
-
const value = opts[key];
|
|
82
|
-
return typeof value === "number" && Number.isFinite(value) && !Number.isNaN(value)
|
|
83
|
-
? value
|
|
84
|
-
: undefined;
|
|
85
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TTS 内置模型目录。
|
|
3
|
-
*
|
|
4
|
-
* 关键点(中文)
|
|
5
|
-
* - 默认主推 `Qwen 0.6B` 与 `Kokoro`。
|
|
6
|
-
* - 目录只保留 Console/UI 需要的稳定元数据,不暴露底层实现细节。
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { TtsModelCatalogItem, TtsModelId } from "@/tts/types/Tts.js";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* 内置 TTS 模型目录。
|
|
13
|
-
*/
|
|
14
|
-
export const TTS_MODEL_CATALOG: readonly TtsModelCatalogItem[] = [
|
|
15
|
-
{
|
|
16
|
-
id: "qwen3-tts-0.6b",
|
|
17
|
-
label: "Qwen3-TTS 0.6B",
|
|
18
|
-
description: "中英文与多语言能力更强,作为主推荐模型。",
|
|
19
|
-
family: "qwen3",
|
|
20
|
-
recommended: true,
|
|
21
|
-
assets: [
|
|
22
|
-
{
|
|
23
|
-
repoId: "Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice",
|
|
24
|
-
revision: "main",
|
|
25
|
-
},
|
|
26
|
-
],
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: "kokoro-82m",
|
|
30
|
-
label: "Kokoro 82M",
|
|
31
|
-
description: "体积更小,启动更轻,适合本地快速合成。",
|
|
32
|
-
family: "kokoro",
|
|
33
|
-
recommended: true,
|
|
34
|
-
assets: [
|
|
35
|
-
{
|
|
36
|
-
repoId: "hexgrad/Kokoro-82M",
|
|
37
|
-
revision: "main",
|
|
38
|
-
files: [
|
|
39
|
-
"config.json",
|
|
40
|
-
"kokoro-v1_0.pth",
|
|
41
|
-
"voices/af_heart.pt",
|
|
42
|
-
"voices/zf_xiaoni.pt",
|
|
43
|
-
],
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: "qwen3-tts-1.7b",
|
|
49
|
-
label: "Qwen3-TTS 1.7B",
|
|
50
|
-
description: "效果更强,但体积更大,适合更看重质量的本地合成。",
|
|
51
|
-
family: "qwen3",
|
|
52
|
-
recommended: false,
|
|
53
|
-
assets: [
|
|
54
|
-
{
|
|
55
|
-
repoId: "Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice",
|
|
56
|
-
revision: "main",
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
},
|
|
60
|
-
] as const;
|
|
61
|
-
|
|
62
|
-
const TTS_MODEL_ALIAS_MAP: Record<string, TtsModelId> = {
|
|
63
|
-
"qwen3-tts-0.6b": "qwen3-tts-0.6b",
|
|
64
|
-
qwen3tts06b: "qwen3-tts-0.6b",
|
|
65
|
-
qwen_06b: "qwen3-tts-0.6b",
|
|
66
|
-
"kokoro-82m": "kokoro-82m",
|
|
67
|
-
kokoro82m: "kokoro-82m",
|
|
68
|
-
"qwen3-tts-1.7b": "qwen3-tts-1.7b",
|
|
69
|
-
qwen3tts17b: "qwen3-tts-1.7b",
|
|
70
|
-
qwen_17b: "qwen3-tts-1.7b",
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const CATALOG_BY_ID: Record<TtsModelId, TtsModelCatalogItem> = TTS_MODEL_CATALOG.reduce(
|
|
74
|
-
(acc, item) => {
|
|
75
|
-
acc[item.id] = item;
|
|
76
|
-
return acc;
|
|
77
|
-
},
|
|
78
|
-
{} as Record<TtsModelId, TtsModelCatalogItem>,
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* 解析用户输入为稳定模型 ID(支持别名与大小写)。
|
|
83
|
-
*/
|
|
84
|
-
export function resolveTtsModelId(input: string): TtsModelId | null {
|
|
85
|
-
const raw = String(input || "").trim();
|
|
86
|
-
if (!raw) return null;
|
|
87
|
-
const exact = TTS_MODEL_CATALOG.find((item) => item.id === raw);
|
|
88
|
-
if (exact) return exact.id;
|
|
89
|
-
return TTS_MODEL_ALIAS_MAP[raw.toLowerCase()] || null;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* 读取目录项;不存在返回 null。
|
|
94
|
-
*/
|
|
95
|
-
export function getTtsModelCatalogItem(id: TtsModelId): TtsModelCatalogItem | null {
|
|
96
|
-
return CATALOG_BY_ID[id] || null;
|
|
97
|
-
}
|