open-agents-ai 0.187.563 → 0.187.565
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/dist/index.js +550 -206
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -533020,99 +533020,50 @@ ${memoryLines.join("\n")}`
|
|
|
533020
533020
|
]);
|
|
533021
533021
|
const REG61_BYPASS_TOOLS = /* @__PURE__ */ new Set([
|
|
533022
533022
|
...REG61_EDIT_TOOLS,
|
|
533023
|
-
// Escape hatches: explicitly allowed while gate is active so
|
|
533024
|
-
// the agent can exit cleanly (task_complete), escalate to human
|
|
533025
|
-
// (ask_user), or complete an explicit web task without deadlock.
|
|
533026
|
-
// shell, file_read, todo_*, grep_search, list_directory are
|
|
533027
|
-
// NOT in bypass — those are the exact patterns batch528/529
|
|
533028
|
-
// agents used to ignore REG-61.
|
|
533029
533023
|
"web_search",
|
|
533030
533024
|
"task_complete",
|
|
533031
533025
|
"ask_user",
|
|
533032
|
-
// DECOMP-1: sub-agent dispatch is productive work (it spawns
|
|
533033
|
-
// a focused-context implementation worker). Block-listing
|
|
533034
|
-
// sub_agent here would defeat the spec-decomposition pattern
|
|
533035
|
-
// — agents would be forced into main-context edits even when
|
|
533036
|
-
// delegation is the right move. sub_agent calls do NOT clear
|
|
533037
|
-
// the gate (we want the agent to also produce direct edits
|
|
533038
|
-
// for orchestration glue / integration), but they're allowed
|
|
533039
|
-
// through.
|
|
533040
533026
|
"sub_agent",
|
|
533041
533027
|
"priority_delegate",
|
|
533042
533028
|
"background_run"
|
|
533043
533029
|
]);
|
|
533044
533030
|
if (this._reg61PerpetualGateActive && !REG61_BYPASS_TOOLS.has(tc.name) && process.env["OA_DISABLE_REG61_COERCE"] !== "1") {
|
|
533045
|
-
this.emit({
|
|
533046
|
-
type: "tool_call",
|
|
533047
|
-
toolName: tc.name,
|
|
533048
|
-
toolArgs: tc.arguments,
|
|
533049
|
-
turn,
|
|
533050
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
533051
|
-
});
|
|
533052
533031
|
const _dbgLoop = this._detectDebugLoop(toolCallLog);
|
|
533053
533032
|
const _debugLoopSampleSafe = (_dbgLoop.repeatedSample ?? "").slice(0, 120);
|
|
533054
533033
|
const _localFailureNudge = this._renderLocalFailureNudge(turn);
|
|
533055
|
-
const
|
|
533056
|
-
`[
|
|
533034
|
+
const reg61SteerMsg = _dbgLoop.detected ? [
|
|
533035
|
+
`[REG-61 directive active — REG-66 DEBUG-LOOP detected]`,
|
|
533057
533036
|
``,
|
|
533058
|
-
`Pattern: ${_dbgLoop.kind === "shell" ? "shell command" : "file"} "${_debugLoopSampleSafe}" was used ${_dbgLoop.count}× in the trailing window with ZERO creative edits landing. You
|
|
533037
|
+
`Pattern: ${_dbgLoop.kind === "shell" ? "shell command" : "file"} "${_debugLoopSampleSafe}" was used ${_dbgLoop.count}× in the trailing window with ZERO creative edits landing. You appear stuck in a debug loop where re-running / re-reading is producing no new information.`,
|
|
533059
533038
|
``,
|
|
533060
|
-
`
|
|
533039
|
+
`Consider PERTURBING: make a speculative edit to get a NEW error signal.`,
|
|
533061
533040
|
``,
|
|
533062
|
-
`Strategy
|
|
533063
|
-
` 1. Pick the source file most likely implicated by the recurring failure
|
|
533041
|
+
`Strategy:`,
|
|
533042
|
+
` 1. Pick the source file most likely implicated by the recurring failure.`,
|
|
533064
533043
|
` 2. Pick ONE plausible cause — most-recently-modified line, most-complex function, most-likely-misnamed import, most-likely off-by-one.`,
|
|
533065
533044
|
` 3. Make a SPECULATIVE edit that changes that thing — even if you are NOT certain it'll fix the bug. The point is to get a NEW error signal that disambiguates.`,
|
|
533066
|
-
` 4. Re-run the failing command. If the error CHANGED, you've learned something
|
|
533045
|
+
` 4. Re-run the failing command. If the error CHANGED, you've learned something.`,
|
|
533067
533046
|
``,
|
|
533068
|
-
`This
|
|
533069
|
-
``,
|
|
533070
|
-
`Issue EXACTLY ONE of: file_write / file_edit / batch_edit / file_patch on a single concrete change. The edit must actually change disk state; dry-runs and no-ops do not clear this directive.`,
|
|
533071
|
-
``,
|
|
533072
|
-
_localFailureNudge,
|
|
533073
|
-
``,
|
|
533074
|
-
`Allowed exits (will not be blocked but will not clear the directive either):`,
|
|
533075
|
-
` * task_complete - exit if you genuinely cannot identify any plausible local perturbation`,
|
|
533076
|
-
` * ask_user - escalate to human (if available)`,
|
|
533077
|
-
``,
|
|
533078
|
-
`Once you make a real edit, the directive clears and you'll see the new test result.`
|
|
533047
|
+
`This tool call was ALLOWED through, but the edit directive remains active.`
|
|
533079
533048
|
].join("\n") : [
|
|
533080
|
-
`[
|
|
533049
|
+
`[REG-61 directive active]`,
|
|
533081
533050
|
``,
|
|
533082
|
-
`A REG-61 FIRST-EDIT NUDGE was issued earlier and has not yet been satisfied. The directive
|
|
533051
|
+
`A REG-61 FIRST-EDIT NUDGE was issued earlier and has not yet been satisfied. The directive asks you to prioritize a creative edit. You issued '${tc.name}' instead, which is a read/explore/shell call.`,
|
|
533083
533052
|
``,
|
|
533084
|
-
`
|
|
533053
|
+
`This call was ALLOWED through (needed context should never be blocked), but try to make a creative edit next:`,
|
|
533085
533054
|
` • file_write — create a new file`,
|
|
533086
533055
|
` • file_edit — modify an existing file (find/replace)`,
|
|
533087
533056
|
` • batch_edit — multiple find/replace edits in one call`,
|
|
533088
533057
|
` • file_patch — apply a version-checked line-range patch`,
|
|
533089
533058
|
``,
|
|
533090
|
-
_localFailureNudge
|
|
533091
|
-
``,
|
|
533092
|
-
`These exits are also allowed while the directive is active (will not be blocked, will not clear the gate):`,
|
|
533093
|
-
` * task_complete - to exit if you cannot make any local progress`,
|
|
533094
|
-
` * ask_user - to escalate to human (if available)`,
|
|
533095
|
-
``,
|
|
533096
|
-
`Until you issue a creative edit that actually changes disk, ALL of these will be BLOCKED again on every turn: file_read, file_explore, list_directory, grep_search, shell, todo_write, todo_read, memory_read, memory_write, etc. Pick the smallest concrete change that moves work forward.`
|
|
533059
|
+
_localFailureNudge
|
|
533097
533060
|
].join("\n");
|
|
533098
|
-
|
|
533099
|
-
type: "tool_result",
|
|
533100
|
-
toolName: tc.name,
|
|
533101
|
-
success: false,
|
|
533102
|
-
content: reg61BlockMsg.slice(0, 120),
|
|
533103
|
-
turn,
|
|
533104
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
533105
|
-
});
|
|
533061
|
+
pushSoftInjection("system", reg61SteerMsg);
|
|
533106
533062
|
this.emit({
|
|
533107
533063
|
type: "status",
|
|
533108
|
-
content: `REG-61
|
|
533064
|
+
content: `REG-61 STEER — nudge injected for '${tc.name}' at turn ${turn}; tool ALLOWED; gate stays active${_dbgLoop.detected ? `; REG-66 debug-loop variant (${_dbgLoop.kind} "${_debugLoopSampleSafe.slice(0, 60)}" ${_dbgLoop.count}×)` : ""}`,
|
|
533109
533065
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
533110
533066
|
});
|
|
533111
|
-
this._tagSyntheticFailure({
|
|
533112
|
-
mode: "step_repetition",
|
|
533113
|
-
rationale: `REG-61 perpetual coercion block on '${tc.name}' — agent ignored FIRST-EDIT NUDGE${_dbgLoop.detected ? " (debug-loop variant)" : ""}`
|
|
533114
|
-
});
|
|
533115
|
-
return { tc, output: reg61BlockMsg };
|
|
533116
533067
|
}
|
|
533117
533068
|
{
|
|
533118
533069
|
const _decomp2Block = this._maybeDecomp2Block(tc, turn);
|
|
@@ -563875,10 +563826,21 @@ __export(voice_exports, {
|
|
|
563875
563826
|
registerCustomOnnxModel: () => registerCustomOnnxModel,
|
|
563876
563827
|
resetNarrationContext: () => resetNarrationContext
|
|
563877
563828
|
});
|
|
563878
|
-
import {
|
|
563829
|
+
import {
|
|
563830
|
+
existsSync as existsSync87,
|
|
563831
|
+
mkdirSync as mkdirSync49,
|
|
563832
|
+
writeFileSync as writeFileSync46,
|
|
563833
|
+
readFileSync as readFileSync71,
|
|
563834
|
+
unlinkSync as unlinkSync17,
|
|
563835
|
+
readdirSync as readdirSync25,
|
|
563836
|
+
statSync as statSync28
|
|
563837
|
+
} from "node:fs";
|
|
563879
563838
|
import { join as join104, dirname as dirname30 } from "node:path";
|
|
563880
563839
|
import { homedir as homedir32, tmpdir as tmpdir20, platform as platform5 } from "node:os";
|
|
563881
|
-
import {
|
|
563840
|
+
import {
|
|
563841
|
+
execSync as execSync51,
|
|
563842
|
+
spawn as nodeSpawn
|
|
563843
|
+
} from "node:child_process";
|
|
563882
563844
|
import { createRequire as createRequire4 } from "node:module";
|
|
563883
563845
|
function sanitizeForTTS(text) {
|
|
563884
563846
|
return text.replace(/^#{1,6}\s+/gm, "").replace(/\*{1,3}([^*]+)\*{1,3}/g, "$1").replace(/_{1,3}([^_]+)_{1,3}/g, "$1").replace(/~~([^~]+)~~/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/```[\s\S]*?```/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1").replace(/^[\s]*[-*+]\s+/gm, "").replace(/^[\s]*\d+\.\s+/gm, "").replace(/^>\s+/gm, "").replace(/^[-*_]{3,}$/gm, "").replace(/\[[ xX]\]\s*/g, "").replace(/[\u{1F600}-\u{1F64F}]/gu, "").replace(/[\u{1F300}-\u{1F5FF}]/gu, "").replace(/[\u{1F680}-\u{1F6FF}]/gu, "").replace(/[\u{1F1E0}-\u{1F1FF}]/gu, "").replace(/[\u{2600}-\u{26FF}]/gu, "").replace(/[\u{2700}-\u{27BF}]/gu, "").replace(/[\u{FE00}-\u{FE0F}]/gu, "").replace(/[\u{1F900}-\u{1F9FF}]/gu, "").replace(/[\u{1FA00}-\u{1FA6F}]/gu, "").replace(/[\u{1FA70}-\u{1FAFF}]/gu, "").replace(/[\u{200D}]/gu, "").replace(/[\u{20E3}]/gu, "").replace(/[✓✔✗✘✕✖⚠️⏸⏹⏵●○◆◇■□▪▫►▼▲◀⬆⬇⬅➡↑↓←→⇐⇒⇑⇓]/g, "").replace(/[─━│┃┌┐└┘├┤┬┴┼╔╗╚╝╠╣╦╩╬⎿⎾▕▏⏐░▒▓█⠀-⣿]/g, "").replace(/\s{2,}/g, " ").trim();
|
|
@@ -563951,7 +563913,12 @@ function normalizeSupertonicSettings(value2) {
|
|
|
563951
563913
|
voiceName: SUPERTONIC_VOICES.includes(String(v.voiceName ?? "")) ? String(v.voiceName) : DEFAULT_SUPERTONIC_SETTINGS.voiceName,
|
|
563952
563914
|
lang: SUPERTONIC_LANGS.includes(String(v.lang ?? "")) ? String(v.lang) : DEFAULT_SUPERTONIC_SETTINGS.lang,
|
|
563953
563915
|
speed: clampFloat(v.speed, DEFAULT_SUPERTONIC_SETTINGS.speed, 0.7, 1.8),
|
|
563954
|
-
totalStep: clampInt(
|
|
563916
|
+
totalStep: clampInt(
|
|
563917
|
+
v.totalStep,
|
|
563918
|
+
DEFAULT_SUPERTONIC_SETTINGS.totalStep,
|
|
563919
|
+
4,
|
|
563920
|
+
16
|
|
563921
|
+
),
|
|
563955
563922
|
expression: isSupertonicExpression(v.expression) ? v.expression : DEFAULT_SUPERTONIC_SETTINGS.expression
|
|
563956
563923
|
};
|
|
563957
563924
|
}
|
|
@@ -564149,7 +564116,9 @@ function extractToolObject(toolName, args) {
|
|
|
564149
564116
|
case "sub_agent": {
|
|
564150
564117
|
const topic = extractSubAgentTopic(args);
|
|
564151
564118
|
if (topic) return topic;
|
|
564152
|
-
const agentType = String(
|
|
564119
|
+
const agentType = String(
|
|
564120
|
+
args["subagent_type"] ?? args["type"] ?? ""
|
|
564121
|
+
).replace(/-/g, " ");
|
|
564153
564122
|
return agentType || "sub agent";
|
|
564154
564123
|
}
|
|
564155
564124
|
case "batch_edit":
|
|
@@ -564225,7 +564194,11 @@ function extractToolVerb(toolName, args) {
|
|
|
564225
564194
|
return ["examining", "looking at", "examined"];
|
|
564226
564195
|
default: {
|
|
564227
564196
|
const readable = toolName.replace(/[_-]/g, " ");
|
|
564228
|
-
return [
|
|
564197
|
+
return [
|
|
564198
|
+
`invoking ${readable}`,
|
|
564199
|
+
`calling ${readable}`,
|
|
564200
|
+
`invoked ${readable}`
|
|
564201
|
+
];
|
|
564229
564202
|
}
|
|
564230
564203
|
}
|
|
564231
564204
|
}
|
|
@@ -564321,13 +564294,16 @@ function extractShellEssence(cmd) {
|
|
|
564321
564294
|
if (npmTest) return "running tests";
|
|
564322
564295
|
const npmBuild = cmd.match(/npm\s+run\s+(\S+)/);
|
|
564323
564296
|
if (npmBuild) return `running ${npmBuild[1]}`;
|
|
564324
|
-
const gitCmd = cmd.match(
|
|
564297
|
+
const gitCmd = cmd.match(
|
|
564298
|
+
/git\s+(commit|push|pull|merge|rebase|checkout|diff|status|log|stash|add)\b/
|
|
564299
|
+
);
|
|
564325
564300
|
if (gitCmd) return `git ${gitCmd[1]}`;
|
|
564326
564301
|
if (/npm\s+publish/.test(cmd)) return "publishing to npm";
|
|
564327
564302
|
const nodeScript = cmd.match(/(?:node|tsx?)\s+(\S+)/);
|
|
564328
564303
|
if (nodeScript) return `running ${nodeScript[1].split("/").pop()}`;
|
|
564329
564304
|
const curlUrl = cmd.match(/curl\s+.*?(https?:\/\/\S{10,40})/);
|
|
564330
|
-
if (curlUrl)
|
|
564305
|
+
if (curlUrl)
|
|
564306
|
+
return `fetching ${curlUrl[1].replace(/https?:\/\//, "").split("/")[0]}`;
|
|
564331
564307
|
const pnpm = cmd.match(/pnpm\s+(-r\s+)?(\S+)/);
|
|
564332
564308
|
if (pnpm) return `pnpm ${pnpm[1] || ""}${pnpm[2]}`.trim();
|
|
564333
564309
|
return "";
|
|
@@ -564385,10 +564361,8 @@ function computeStateInterjection(quadrant, stark) {
|
|
|
564385
564361
|
return "";
|
|
564386
564362
|
}
|
|
564387
564363
|
case 2: {
|
|
564388
|
-
if (errCount >= 3)
|
|
564389
|
-
|
|
564390
|
-
if (errCount === 2)
|
|
564391
|
-
return `Second failure in a row. `;
|
|
564364
|
+
if (errCount >= 3) return `${errCount} consecutive errors now. `;
|
|
564365
|
+
if (errCount === 2) return `Second failure in a row. `;
|
|
564392
564366
|
if (totalErrors >= 5)
|
|
564393
564367
|
return `${totalErrors} errors this session, pushing through. `;
|
|
564394
564368
|
if (totalErrors >= 2 && totalCalls > 5)
|
|
@@ -564400,21 +564374,17 @@ function computeStateInterjection(quadrant, stark) {
|
|
|
564400
564374
|
return `${totalCalls} operations, zero errors. `;
|
|
564401
564375
|
if (uniqueFiles >= 4 && totalErrors === 0)
|
|
564402
564376
|
return `${uniqueFiles} files, all clean. `;
|
|
564403
|
-
if (streak >= 5)
|
|
564404
|
-
return `Steady, ${streak} clean operations. `;
|
|
564377
|
+
if (streak >= 5) return `Steady, ${streak} clean operations. `;
|
|
564405
564378
|
if (totalCalls > 8 && errCount === 0)
|
|
564406
564379
|
return `Smooth at step ${totalCalls}. `;
|
|
564407
564380
|
return "";
|
|
564408
564381
|
}
|
|
564409
564382
|
case 4: {
|
|
564410
|
-
if (uniqueFiles >= 6)
|
|
564411
|
-
return `${uniqueFiles} files in play. `;
|
|
564383
|
+
if (uniqueFiles >= 6) return `${uniqueFiles} files in play. `;
|
|
564412
564384
|
if (totalErrors > 0 && errCount === 0)
|
|
564413
564385
|
return `Past ${totalErrors} error${totalErrors > 1 ? "s" : ""}, being careful. `;
|
|
564414
|
-
if (sameToolCount >= 4)
|
|
564415
|
-
|
|
564416
|
-
if (totalCalls > 10)
|
|
564417
|
-
return `Step ${totalCalls}, measured pace. `;
|
|
564386
|
+
if (sameToolCount >= 4) return `Pass ${sameToolCount} on this file. `;
|
|
564387
|
+
if (totalCalls > 10) return `Step ${totalCalls}, measured pace. `;
|
|
564418
564388
|
return "";
|
|
564419
564389
|
}
|
|
564420
564390
|
default:
|
|
@@ -564471,8 +564441,11 @@ function extractResultDigest(toolName, content) {
|
|
|
564471
564441
|
const nuggets = [];
|
|
564472
564442
|
const ethMatch = text.match(/([\d.]+)\s*ETH/i);
|
|
564473
564443
|
if (ethMatch) nuggets.push(`${ethMatch[1]} ETH`);
|
|
564474
|
-
const tokenMatch = text.match(
|
|
564475
|
-
|
|
564444
|
+
const tokenMatch = text.match(
|
|
564445
|
+
/([\d,.]+)\s*(USDC|USDT|DAI|BTC|SOL|MATIC|tokens?)\b/i
|
|
564446
|
+
);
|
|
564447
|
+
if (tokenMatch && !nuggets.length)
|
|
564448
|
+
nuggets.push(`${tokenMatch[1]} ${tokenMatch[2]}`);
|
|
564476
564449
|
const addrMatch = text.match(/(0x[0-9a-fA-F]{8})[0-9a-fA-F]+/);
|
|
564477
564450
|
if (addrMatch) nuggets.push(`address ${addrMatch[1]}...`);
|
|
564478
564451
|
const httpMatch = text.match(/(?:status|code)[:\s]*(\d{3})\b/i);
|
|
@@ -564487,7 +564460,9 @@ function extractResultDigest(toolName, content) {
|
|
|
564487
564460
|
}
|
|
564488
564461
|
const errMatch = text.match(/(?:error|Error|ERROR)[:\s]+(.{5,50}?)(?:\n|$)/);
|
|
564489
564462
|
if (errMatch) nuggets.push(`error: ${errMatch[1].trim()}`);
|
|
564490
|
-
const statusMatch = text.match(
|
|
564463
|
+
const statusMatch = text.match(
|
|
564464
|
+
/\[(running|complete|completed|failed|pending|success|stopped|error)\]/i
|
|
564465
|
+
);
|
|
564491
564466
|
if (statusMatch) nuggets.push(statusMatch[1].toLowerCase());
|
|
564492
564467
|
const versionMatch = text.match(/\bv?(\d+\.\d+\.\d+)\b/);
|
|
564493
564468
|
if (versionMatch && !nuggets.some((n2) => n2.includes(versionMatch[1]))) {
|
|
@@ -564500,7 +564475,8 @@ function extractResultDigest(toolName, content) {
|
|
|
564500
564475
|
const gasMatch = text.match(/gas[:\s]*([\d,]+)/i);
|
|
564501
564476
|
if (gasMatch) nuggets.push(`gas ${gasMatch[1]}`);
|
|
564502
564477
|
const exitMatch = text.match(/exit\s*(?:code)?[:\s]*(\d+)/i);
|
|
564503
|
-
if (exitMatch && exitMatch[1] !== "0")
|
|
564478
|
+
if (exitMatch && exitMatch[1] !== "0")
|
|
564479
|
+
nuggets.push(`exit code ${exitMatch[1]}`);
|
|
564504
564480
|
if (nuggets.length === 0) {
|
|
564505
564481
|
const contentDigest = extractContentSummary(text, toolName);
|
|
564506
564482
|
if (contentDigest) nuggets.push(contentDigest);
|
|
@@ -564509,7 +564485,10 @@ function extractResultDigest(toolName, content) {
|
|
|
564509
564485
|
return digest3.length > 100 ? digest3.slice(0, 97) + "..." : digest3;
|
|
564510
564486
|
}
|
|
564511
564487
|
function extractContentSummary(text, toolName) {
|
|
564512
|
-
let cleaned = text.replace(/^\s*\d+[│|:→]\s*/gm, "").replace(/\d{4}-\d{2}-\d{2}T[\d:.]+Z?\s*/g, "").replace(/^[\s*#=-]+$/gm, "").replace(/```[\s\S]*?```/g, "").replace(
|
|
564488
|
+
let cleaned = text.replace(/^\s*\d+[│|:→]\s*/gm, "").replace(/\d{4}-\d{2}-\d{2}T[\d:.]+Z?\s*/g, "").replace(/^[\s*#=-]+$/gm, "").replace(/```[\s\S]*?```/g, "").replace(
|
|
564489
|
+
/^\s*(import|export|const|let|var|function|class|interface|type)\s/gm,
|
|
564490
|
+
""
|
|
564491
|
+
).replace(/[{}()\[\];]/g, "").trim();
|
|
564513
564492
|
const lines = cleaned.split("\n").map((l2) => l2.trim()).filter((l2) => l2.length > 10);
|
|
564514
564493
|
for (const line of lines) {
|
|
564515
564494
|
const wordChars = line.replace(/[^a-zA-Z\s]/g, "").trim();
|
|
@@ -564528,7 +564507,9 @@ function extractContentSummary(text, toolName) {
|
|
|
564528
564507
|
return summary;
|
|
564529
564508
|
}
|
|
564530
564509
|
if (toolName === "sub_agent") {
|
|
564531
|
-
const summaryMatch = text.match(
|
|
564510
|
+
const summaryMatch = text.match(
|
|
564511
|
+
/(?:summary|created|completed|result)[:\s]+(.{10,80}?)(?:\n|$)/i
|
|
564512
|
+
);
|
|
564532
564513
|
if (summaryMatch) {
|
|
564533
564514
|
let s2 = summaryMatch[1].trim();
|
|
564534
564515
|
if (s2.length > 0) s2 = s2.charAt(0).toLowerCase() + s2.slice(1);
|
|
@@ -564709,8 +564690,51 @@ var init_voice = __esm({
|
|
|
564709
564690
|
supertonicLang: "en"
|
|
564710
564691
|
}
|
|
564711
564692
|
};
|
|
564712
|
-
SUPERTONIC_LANGS = [
|
|
564713
|
-
|
|
564693
|
+
SUPERTONIC_LANGS = [
|
|
564694
|
+
"en",
|
|
564695
|
+
"ko",
|
|
564696
|
+
"ja",
|
|
564697
|
+
"ar",
|
|
564698
|
+
"bg",
|
|
564699
|
+
"cs",
|
|
564700
|
+
"da",
|
|
564701
|
+
"de",
|
|
564702
|
+
"el",
|
|
564703
|
+
"es",
|
|
564704
|
+
"et",
|
|
564705
|
+
"fi",
|
|
564706
|
+
"fr",
|
|
564707
|
+
"hi",
|
|
564708
|
+
"hr",
|
|
564709
|
+
"hu",
|
|
564710
|
+
"id",
|
|
564711
|
+
"it",
|
|
564712
|
+
"lt",
|
|
564713
|
+
"lv",
|
|
564714
|
+
"nl",
|
|
564715
|
+
"pl",
|
|
564716
|
+
"pt",
|
|
564717
|
+
"ro",
|
|
564718
|
+
"ru",
|
|
564719
|
+
"sk",
|
|
564720
|
+
"sl",
|
|
564721
|
+
"sv",
|
|
564722
|
+
"tr",
|
|
564723
|
+
"uk",
|
|
564724
|
+
"vi"
|
|
564725
|
+
];
|
|
564726
|
+
SUPERTONIC_VOICES = [
|
|
564727
|
+
"M1",
|
|
564728
|
+
"M2",
|
|
564729
|
+
"M3",
|
|
564730
|
+
"M4",
|
|
564731
|
+
"M5",
|
|
564732
|
+
"F1",
|
|
564733
|
+
"F2",
|
|
564734
|
+
"F3",
|
|
564735
|
+
"F4",
|
|
564736
|
+
"F5"
|
|
564737
|
+
];
|
|
564714
564738
|
DEFAULT_SUPERTONIC_SETTINGS = {
|
|
564715
564739
|
voiceName: "M4",
|
|
564716
564740
|
lang: "en",
|
|
@@ -564803,7 +564827,9 @@ except Exception as exc:
|
|
|
564803
564827
|
/** True when current model uses Supertonic3 backend */
|
|
564804
564828
|
supertonicActive = false;
|
|
564805
564829
|
supertonicInstalled = null;
|
|
564806
|
-
supertonicSettings = {
|
|
564830
|
+
supertonicSettings = {
|
|
564831
|
+
...DEFAULT_SUPERTONIC_SETTINGS
|
|
564832
|
+
};
|
|
564807
564833
|
/** Whether LuxTTS voice-clone backend is currently active */
|
|
564808
564834
|
get isLuxtts() {
|
|
564809
564835
|
return this.luxttsActive;
|
|
@@ -564884,17 +564910,25 @@ except Exception as exc:
|
|
|
564884
564910
|
const b = VOICE_MODELS[k].backend;
|
|
564885
564911
|
return !b || b === "onnx";
|
|
564886
564912
|
});
|
|
564887
|
-
const mlxModels = Object.keys(VOICE_MODELS).filter(
|
|
564888
|
-
|
|
564889
|
-
|
|
564913
|
+
const mlxModels = Object.keys(VOICE_MODELS).filter(
|
|
564914
|
+
(k) => VOICE_MODELS[k].backend === "mlx"
|
|
564915
|
+
);
|
|
564916
|
+
const luxttsModels = Object.keys(VOICE_MODELS).filter(
|
|
564917
|
+
(k) => VOICE_MODELS[k].backend === "luxtts"
|
|
564918
|
+
);
|
|
564919
|
+
const supertonicModels = Object.keys(VOICE_MODELS).filter(
|
|
564920
|
+
(k) => VOICE_MODELS[k].backend === "supertonic"
|
|
564921
|
+
);
|
|
564890
564922
|
let msg = `Unknown voice model: "${id}". Available:`;
|
|
564891
564923
|
msg += `
|
|
564892
564924
|
ONNX: ${onnxModels.join(", ")}`;
|
|
564893
564925
|
if (mlxModels.length) msg += `
|
|
564894
564926
|
MLX (macOS): ${mlxModels.join(", ")}`;
|
|
564895
|
-
if (luxttsModels.length)
|
|
564927
|
+
if (luxttsModels.length)
|
|
564928
|
+
msg += `
|
|
564896
564929
|
LuxTTS (voice clone): ${luxttsModels.join(", ")}`;
|
|
564897
|
-
if (supertonicModels.length)
|
|
564930
|
+
if (supertonicModels.length)
|
|
564931
|
+
msg += `
|
|
564898
564932
|
Supertonic3: ${supertonicModels.join(", ")}`;
|
|
564899
564933
|
return msg;
|
|
564900
564934
|
}
|
|
@@ -565045,7 +565079,15 @@ except Exception as exc:
|
|
|
565045
565079
|
writeFileSync46(_VoiceEngine.cloneMetaFile(), JSON.stringify(meta, null, 2));
|
|
565046
565080
|
}
|
|
565047
565081
|
/** Audio file extensions recognized as clone references */
|
|
565048
|
-
static AUDIO_EXTS = /* @__PURE__ */ new Set([
|
|
565082
|
+
static AUDIO_EXTS = /* @__PURE__ */ new Set([
|
|
565083
|
+
"wav",
|
|
565084
|
+
"mp3",
|
|
565085
|
+
"ogg",
|
|
565086
|
+
"flac",
|
|
565087
|
+
"m4a",
|
|
565088
|
+
"opus",
|
|
565089
|
+
"aac"
|
|
565090
|
+
]);
|
|
565049
565091
|
/**
|
|
565050
565092
|
* List all clone reference audio files with metadata.
|
|
565051
565093
|
* Returns array of { filename, path, name, size, isActive }.
|
|
@@ -565148,7 +565190,13 @@ except Exception as exc:
|
|
|
565148
565190
|
return;
|
|
565149
565191
|
}
|
|
565150
565192
|
for (const chunk of chunks) {
|
|
565151
|
-
this.speakQueue.push({
|
|
565193
|
+
this.speakQueue.push({
|
|
565194
|
+
text: chunk,
|
|
565195
|
+
volume,
|
|
565196
|
+
pitchFactor,
|
|
565197
|
+
speedFactor,
|
|
565198
|
+
stereoDelayMs
|
|
565199
|
+
});
|
|
565152
565200
|
}
|
|
565153
565201
|
if (!this.speaking) {
|
|
565154
565202
|
this.drainQueue().catch(() => {
|
|
@@ -565378,10 +565426,18 @@ except Exception as exc:
|
|
|
565378
565426
|
);
|
|
565379
565427
|
}
|
|
565380
565428
|
if (!wavPath) {
|
|
565381
|
-
wavPath = await this.synthesizeLuxttsWav(
|
|
565429
|
+
wavPath = await this.synthesizeLuxttsWav(
|
|
565430
|
+
item.text,
|
|
565431
|
+
item.speedFactor
|
|
565432
|
+
);
|
|
565382
565433
|
}
|
|
565383
565434
|
if (wavPath) {
|
|
565384
|
-
await this.postProcessAndPlayLuxtts(
|
|
565435
|
+
await this.postProcessAndPlayLuxtts(
|
|
565436
|
+
wavPath,
|
|
565437
|
+
item.volume,
|
|
565438
|
+
item.pitchFactor,
|
|
565439
|
+
item.stereoDelayMs
|
|
565440
|
+
);
|
|
565385
565441
|
}
|
|
565386
565442
|
if (prefetchPromise && nextItem) {
|
|
565387
565443
|
try {
|
|
@@ -565393,7 +565449,13 @@ except Exception as exc:
|
|
|
565393
565449
|
}
|
|
565394
565450
|
}
|
|
565395
565451
|
} else {
|
|
565396
|
-
await this.synthesizeAndPlay(
|
|
565452
|
+
await this.synthesizeAndPlay(
|
|
565453
|
+
item.text,
|
|
565454
|
+
item.volume,
|
|
565455
|
+
item.pitchFactor,
|
|
565456
|
+
item.speedFactor,
|
|
565457
|
+
item.stereoDelayMs
|
|
565458
|
+
);
|
|
565397
565459
|
}
|
|
565398
565460
|
} catch {
|
|
565399
565461
|
}
|
|
@@ -565422,11 +565484,23 @@ except Exception as exc:
|
|
|
565422
565484
|
// -------------------------------------------------------------------------
|
|
565423
565485
|
async synthesizeAndPlay(text, volume = 1, pitchFactor = 1, speedFactor = 1, stereoDelayMs = 0.6) {
|
|
565424
565486
|
if (this.luxttsActive) {
|
|
565425
|
-
await this.synthesizeWithLuxtts(
|
|
565487
|
+
await this.synthesizeWithLuxtts(
|
|
565488
|
+
text,
|
|
565489
|
+
volume,
|
|
565490
|
+
pitchFactor,
|
|
565491
|
+
speedFactor,
|
|
565492
|
+
stereoDelayMs
|
|
565493
|
+
);
|
|
565426
565494
|
return;
|
|
565427
565495
|
}
|
|
565428
565496
|
if (this.supertonicActive) {
|
|
565429
|
-
await this.synthesizeWithSupertonic(
|
|
565497
|
+
await this.synthesizeWithSupertonic(
|
|
565498
|
+
text,
|
|
565499
|
+
volume,
|
|
565500
|
+
pitchFactor,
|
|
565501
|
+
speedFactor,
|
|
565502
|
+
stereoDelayMs
|
|
565503
|
+
);
|
|
565430
565504
|
return;
|
|
565431
565505
|
}
|
|
565432
565506
|
if (this.mlxActive) {
|
|
@@ -565601,9 +565675,15 @@ except Exception as exc:
|
|
|
565601
565675
|
for (let i2 = 0; i2 < numSamples; i2++) {
|
|
565602
565676
|
const lSample = Math.max(-1, Math.min(1, left[i2]));
|
|
565603
565677
|
const rSample = Math.max(-1, Math.min(1, right[i2]));
|
|
565604
|
-
buffer2.writeInt16LE(
|
|
565678
|
+
buffer2.writeInt16LE(
|
|
565679
|
+
lSample < 0 ? lSample * 32768 : lSample * 32767,
|
|
565680
|
+
pos
|
|
565681
|
+
);
|
|
565605
565682
|
pos += 2;
|
|
565606
|
-
buffer2.writeInt16LE(
|
|
565683
|
+
buffer2.writeInt16LE(
|
|
565684
|
+
rSample < 0 ? rSample * 32768 : rSample * 32767,
|
|
565685
|
+
pos
|
|
565686
|
+
);
|
|
565607
565687
|
pos += 2;
|
|
565608
565688
|
}
|
|
565609
565689
|
writeFileSync46(path11, buffer2);
|
|
@@ -565624,7 +565704,9 @@ except Exception as exc:
|
|
|
565624
565704
|
const phonemes = await this.phonemizeFn(text, voice);
|
|
565625
565705
|
const phonemeText = Array.isArray(phonemes) ? phonemes.join(" ") : String(phonemes || text);
|
|
565626
565706
|
const sentences = phonemeText.split(/[.!?]+/).filter((s2) => s2.trim().length > 0);
|
|
565627
|
-
return sentences.map(
|
|
565707
|
+
return sentences.map(
|
|
565708
|
+
(sentence) => Array.from(sentence.trim().normalize("NFD"))
|
|
565709
|
+
);
|
|
565628
565710
|
}
|
|
565629
565711
|
/**
|
|
565630
565712
|
* Convert phoneme character arrays to integer IDs using the model's phoneme_id_map.
|
|
@@ -565676,7 +565758,10 @@ except Exception as exc:
|
|
|
565676
565758
|
buffer2.writeUInt16LE(bitsPerSample, 34);
|
|
565677
565759
|
buffer2.write("data", 36);
|
|
565678
565760
|
buffer2.writeUInt32LE(dataSize, 40);
|
|
565679
|
-
Buffer.from(int16.buffer, int16.byteOffset, int16.byteLength).copy(
|
|
565761
|
+
Buffer.from(int16.buffer, int16.byteOffset, int16.byteLength).copy(
|
|
565762
|
+
buffer2,
|
|
565763
|
+
44
|
|
565764
|
+
);
|
|
565680
565765
|
return buffer2;
|
|
565681
565766
|
}
|
|
565682
565767
|
writeWav(samples, sampleRate, path11) {
|
|
@@ -565752,7 +565837,9 @@ except Exception as exc:
|
|
|
565752
565837
|
setSupertonicSettings(patch) {
|
|
565753
565838
|
const current = this.getSupertonicSettings();
|
|
565754
565839
|
const next = {
|
|
565755
|
-
voiceName: SUPERTONIC_VOICES.includes(
|
|
565840
|
+
voiceName: SUPERTONIC_VOICES.includes(
|
|
565841
|
+
String(patch.voiceName ?? current.voiceName)
|
|
565842
|
+
) ? String(patch.voiceName ?? current.voiceName) : current.voiceName,
|
|
565756
565843
|
lang: SUPERTONIC_LANGS.includes(String(patch.lang ?? current.lang)) ? String(patch.lang ?? current.lang) : current.lang,
|
|
565757
565844
|
speed: clampFloat(patch.speed, current.speed, 0.7, 1.8),
|
|
565758
565845
|
totalStep: clampInt(patch.totalStep, current.totalStep, 4, 16),
|
|
@@ -565766,7 +565853,10 @@ except Exception as exc:
|
|
|
565766
565853
|
}
|
|
565767
565854
|
listSupertonicProfiles() {
|
|
565768
565855
|
const store2 = this.loadSupertonicStore();
|
|
565769
|
-
return Object.entries(store2.profiles).map(([name10, settings]) => ({
|
|
565856
|
+
return Object.entries(store2.profiles).map(([name10, settings]) => ({
|
|
565857
|
+
name: name10,
|
|
565858
|
+
settings
|
|
565859
|
+
}));
|
|
565770
565860
|
}
|
|
565771
565861
|
saveSupertonicProfile(name10) {
|
|
565772
565862
|
const clean3 = name10.trim().replace(/[^a-zA-Z0-9_.-]/g, "-");
|
|
@@ -565793,7 +565883,9 @@ except Exception as exc:
|
|
|
565793
565883
|
}
|
|
565794
565884
|
loadSupertonicStore() {
|
|
565795
565885
|
try {
|
|
565796
|
-
const raw = JSON.parse(
|
|
565886
|
+
const raw = JSON.parse(
|
|
565887
|
+
readFileSync71(supertonicProfilesFile(), "utf-8")
|
|
565888
|
+
);
|
|
565797
565889
|
const profiles = {};
|
|
565798
565890
|
for (const [name10, settings] of Object.entries(raw.profiles ?? {})) {
|
|
565799
565891
|
profiles[name10] = normalizeSupertonicSettings(settings);
|
|
@@ -565808,12 +565900,18 @@ except Exception as exc:
|
|
|
565808
565900
|
}
|
|
565809
565901
|
saveSupertonicStore(store2) {
|
|
565810
565902
|
mkdirSync49(voiceDir(), { recursive: true });
|
|
565811
|
-
writeFileSync46(
|
|
565903
|
+
writeFileSync46(
|
|
565904
|
+
supertonicProfilesFile(),
|
|
565905
|
+
JSON.stringify(store2, null, 2),
|
|
565906
|
+
"utf-8"
|
|
565907
|
+
);
|
|
565812
565908
|
}
|
|
565813
565909
|
async ensureSupertonic() {
|
|
565814
565910
|
const py = await this.findPython3Async();
|
|
565815
565911
|
if (!py) {
|
|
565816
|
-
throw new Error(
|
|
565912
|
+
throw new Error(
|
|
565913
|
+
"python3 not found. Install Python 3.10+ to use Supertonic3."
|
|
565914
|
+
);
|
|
565817
565915
|
}
|
|
565818
565916
|
const venvDir = supertonicVenvDir();
|
|
565819
565917
|
const venvPy = supertonicVenvPy();
|
|
@@ -565823,8 +565921,14 @@ except Exception as exc:
|
|
|
565823
565921
|
}
|
|
565824
565922
|
if (!this.checkSupertonicInstalled()) {
|
|
565825
565923
|
renderInfo2("Installing Supertonic3 TTS (first-time setup)...");
|
|
565826
|
-
await this.asyncShell(
|
|
565827
|
-
|
|
565924
|
+
await this.asyncShell(
|
|
565925
|
+
`${JSON.stringify(venvPy)} -m pip install --quiet --upgrade pip`,
|
|
565926
|
+
12e4
|
|
565927
|
+
);
|
|
565928
|
+
await this.asyncShell(
|
|
565929
|
+
`${JSON.stringify(venvPy)} -m pip install --quiet supertonic`,
|
|
565930
|
+
6e5
|
|
565931
|
+
);
|
|
565828
565932
|
this.supertonicInstalled = true;
|
|
565829
565933
|
}
|
|
565830
565934
|
this.writeSupertonicInferScript();
|
|
@@ -565837,7 +565941,10 @@ except Exception as exc:
|
|
|
565837
565941
|
return false;
|
|
565838
565942
|
}
|
|
565839
565943
|
try {
|
|
565840
|
-
execSync51(`${JSON.stringify(venvPy)} -c "import supertonic"`, {
|
|
565944
|
+
execSync51(`${JSON.stringify(venvPy)} -c "import supertonic"`, {
|
|
565945
|
+
stdio: "pipe",
|
|
565946
|
+
timeout: 1e4
|
|
565947
|
+
});
|
|
565841
565948
|
this.supertonicInstalled = true;
|
|
565842
565949
|
return true;
|
|
565843
565950
|
} catch {
|
|
@@ -565862,7 +565969,11 @@ except Exception as exc:
|
|
|
565862
565969
|
child.kill("SIGKILL");
|
|
565863
565970
|
} catch {
|
|
565864
565971
|
}
|
|
565865
|
-
reject(
|
|
565972
|
+
reject(
|
|
565973
|
+
new Error(
|
|
565974
|
+
`Supertonic3 synthesis timed out after ${Math.round(timeoutMs / 1e3)}s`
|
|
565975
|
+
)
|
|
565976
|
+
);
|
|
565866
565977
|
}, timeoutMs);
|
|
565867
565978
|
child.stdout?.on("data", (d2) => {
|
|
565868
565979
|
stdout += d2.toString();
|
|
@@ -565879,10 +565990,22 @@ except Exception as exc:
|
|
|
565879
565990
|
const line = stdout.trim().split("\n").pop() ?? "";
|
|
565880
565991
|
try {
|
|
565881
565992
|
const parsed = JSON.parse(line);
|
|
565882
|
-
if (parsed.ok === false)
|
|
565993
|
+
if (parsed.ok === false)
|
|
565994
|
+
reject(
|
|
565995
|
+
new Error(
|
|
565996
|
+
String(parsed.error ?? (stderr || "Supertonic3 failed"))
|
|
565997
|
+
)
|
|
565998
|
+
);
|
|
565883
565999
|
else resolve43(parsed);
|
|
565884
566000
|
} catch {
|
|
565885
|
-
reject(
|
|
566001
|
+
reject(
|
|
566002
|
+
new Error(
|
|
566003
|
+
(stderr || stdout || "Supertonic3 returned no JSON").slice(
|
|
566004
|
+
0,
|
|
566005
|
+
500
|
|
566006
|
+
)
|
|
566007
|
+
)
|
|
566008
|
+
);
|
|
565886
566009
|
}
|
|
565887
566010
|
});
|
|
565888
566011
|
child.stdin?.end(JSON.stringify(req2));
|
|
@@ -565891,9 +566014,15 @@ except Exception as exc:
|
|
|
565891
566014
|
async synthesizeSupertonicWav(text, speedFactor = 1) {
|
|
565892
566015
|
await this.ensureSupertonic();
|
|
565893
566016
|
const settings = this.getSupertonicSettings();
|
|
565894
|
-
const cleaned = applySupertonicExpression(
|
|
566017
|
+
const cleaned = applySupertonicExpression(
|
|
566018
|
+
text.replace(/\*/g, "").trim(),
|
|
566019
|
+
settings.expression
|
|
566020
|
+
);
|
|
565895
566021
|
if (!cleaned) return null;
|
|
565896
|
-
const wavPath = join104(
|
|
566022
|
+
const wavPath = join104(
|
|
566023
|
+
tmpdir20(),
|
|
566024
|
+
`oa-supertonic3-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.wav`
|
|
566025
|
+
);
|
|
565897
566026
|
try {
|
|
565898
566027
|
await this.supertonicRequest({
|
|
565899
566028
|
text: cleaned,
|
|
@@ -565911,7 +566040,12 @@ except Exception as exc:
|
|
|
565911
566040
|
async synthesizeWithSupertonic(text, volume = 1, pitchFactor = 1, speedFactor = 1, stereoDelayMs = 0.6) {
|
|
565912
566041
|
const wavPath = await this.synthesizeSupertonicWav(text, speedFactor);
|
|
565913
566042
|
if (!wavPath) return;
|
|
565914
|
-
await this.postProcessAndPlayLuxtts(
|
|
566043
|
+
await this.postProcessAndPlayLuxtts(
|
|
566044
|
+
wavPath,
|
|
566045
|
+
volume,
|
|
566046
|
+
pitchFactor,
|
|
566047
|
+
stereoDelayMs
|
|
566048
|
+
);
|
|
565915
566049
|
}
|
|
565916
566050
|
async synthesizeSupertonicToBuffer(text) {
|
|
565917
566051
|
const wavPath = await this.synthesizeSupertonicWav(text, 1);
|
|
@@ -565999,7 +566133,10 @@ except Exception as exc:
|
|
|
565999
566133
|
return false;
|
|
566000
566134
|
}
|
|
566001
566135
|
try {
|
|
566002
|
-
execSync51(`${py} -c "import mlx_audio"`, {
|
|
566136
|
+
execSync51(`${py} -c "import mlx_audio"`, {
|
|
566137
|
+
stdio: "pipe",
|
|
566138
|
+
timeout: 1e4
|
|
566139
|
+
});
|
|
566003
566140
|
this.mlxInstalled = true;
|
|
566004
566141
|
return true;
|
|
566005
566142
|
} catch {
|
|
@@ -566030,7 +566167,10 @@ except Exception as exc:
|
|
|
566030
566167
|
this.mlxInstalled = true;
|
|
566031
566168
|
} catch (err) {
|
|
566032
566169
|
try {
|
|
566033
|
-
await this.asyncShell(
|
|
566170
|
+
await this.asyncShell(
|
|
566171
|
+
`${py} -m pip install mlx-audio --user --quiet`,
|
|
566172
|
+
3e5
|
|
566173
|
+
);
|
|
566034
566174
|
this.mlxInstalled = true;
|
|
566035
566175
|
} catch (err2) {
|
|
566036
566176
|
throw new Error(
|
|
@@ -566091,7 +566231,10 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566091
566231
|
for (let i2 = 0; i2 < samples.length; i2++) {
|
|
566092
566232
|
samples[i2] = Math.round(samples[i2] * volume);
|
|
566093
566233
|
}
|
|
566094
|
-
const scaled = Buffer.concat([
|
|
566234
|
+
const scaled = Buffer.concat([
|
|
566235
|
+
header,
|
|
566236
|
+
Buffer.from(samples.buffer, samples.byteOffset, samples.byteLength)
|
|
566237
|
+
]);
|
|
566095
566238
|
writeFileSync46(wavPath, scaled);
|
|
566096
566239
|
}
|
|
566097
566240
|
} catch {
|
|
@@ -566101,7 +566244,11 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566101
566244
|
try {
|
|
566102
566245
|
const wavData = readFileSync71(wavPath);
|
|
566103
566246
|
if (wavData.length > 44) {
|
|
566104
|
-
const pcm = Buffer.from(
|
|
566247
|
+
const pcm = Buffer.from(
|
|
566248
|
+
wavData.buffer,
|
|
566249
|
+
wavData.byteOffset + 44,
|
|
566250
|
+
wavData.length - 44
|
|
566251
|
+
);
|
|
566105
566252
|
const sampleRate = wavData.readUInt32LE(24);
|
|
566106
566253
|
this.onPCMOutput(pcm, sampleRate);
|
|
566107
566254
|
}
|
|
@@ -566198,13 +566345,18 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566198
566345
|
15e3
|
|
566199
566346
|
).then(async (torchCheck) => {
|
|
566200
566347
|
if (torchCheck === "cpu") {
|
|
566201
|
-
renderWarning2(
|
|
566348
|
+
renderWarning2(
|
|
566349
|
+
"GPU detected but PyTorch is CPU-only. Reinstalling with CUDA support in background..."
|
|
566350
|
+
);
|
|
566202
566351
|
try {
|
|
566203
566352
|
const detectScript = join104(voiceDir(), "detect-torch.py");
|
|
566204
566353
|
writeDetectTorchScript(detectScript);
|
|
566205
566354
|
let pipArgs = `torch torchaudio --index-url https://download.pytorch.org/whl/cu124`;
|
|
566206
566355
|
try {
|
|
566207
|
-
const args = await this.asyncShell(
|
|
566356
|
+
const args = await this.asyncShell(
|
|
566357
|
+
`python3 ${JSON.stringify(detectScript)} --pip-args`,
|
|
566358
|
+
1e4
|
|
566359
|
+
);
|
|
566208
566360
|
if (args.trim()) pipArgs = args.trim();
|
|
566209
566361
|
} catch {
|
|
566210
566362
|
}
|
|
@@ -566226,11 +566378,16 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566226
566378
|
renderWarning2("LuxTTS venv found but import failed. Reinstalling...");
|
|
566227
566379
|
}
|
|
566228
566380
|
}
|
|
566229
|
-
renderInfo2(
|
|
566381
|
+
renderInfo2(
|
|
566382
|
+
"Setting up LuxTTS voice cloning (first-time setup, this takes several minutes)..."
|
|
566383
|
+
);
|
|
566230
566384
|
if (!existsSync87(venvDir)) {
|
|
566231
566385
|
renderInfo2(" Creating Python virtual environment...");
|
|
566232
566386
|
try {
|
|
566233
|
-
await this.asyncShell(
|
|
566387
|
+
await this.asyncShell(
|
|
566388
|
+
`${py} -m venv ${JSON.stringify(venvDir)}`,
|
|
566389
|
+
6e4
|
|
566390
|
+
);
|
|
566234
566391
|
} catch (err) {
|
|
566235
566392
|
throw new Error(
|
|
566236
566393
|
`Failed to create venv: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -566243,7 +566400,10 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566243
566400
|
let pipArgsStr = "torch torchaudio";
|
|
566244
566401
|
let torchDesc = "unknown platform";
|
|
566245
566402
|
try {
|
|
566246
|
-
const detectResult = await this.asyncShell(
|
|
566403
|
+
const detectResult = await this.asyncShell(
|
|
566404
|
+
`${py} ${JSON.stringify(detectScript)} --json`,
|
|
566405
|
+
15e3
|
|
566406
|
+
);
|
|
566247
566407
|
const spec = JSON.parse(detectResult.trim());
|
|
566248
566408
|
pipArgsStr = spec.pip_args_str || "torch torchaudio";
|
|
566249
566409
|
torchDesc = spec.description || `${spec.platform} ${spec.arch} ${spec.accelerator}`;
|
|
@@ -566304,7 +566464,9 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566304
566464
|
const pipCmd = JSON.stringify(venvPy);
|
|
566305
566465
|
const isArm = process.arch === "arm64" || process.arch === "arm";
|
|
566306
566466
|
if (isArm) {
|
|
566307
|
-
renderInfo2(
|
|
566467
|
+
renderInfo2(
|
|
566468
|
+
" ARM device detected — installing build prerequisites then deps individually."
|
|
566469
|
+
);
|
|
566308
566470
|
const isMac = process.platform === "darwin";
|
|
566309
566471
|
try {
|
|
566310
566472
|
const { spawnSync: spawnSync7 } = await import("node:child_process");
|
|
@@ -566312,23 +566474,38 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566312
566474
|
process.stdout.write("\x1B[?1002l\x1B[?1003l\x1B[?1006l");
|
|
566313
566475
|
}
|
|
566314
566476
|
if (isMac) {
|
|
566315
|
-
renderInfo2(
|
|
566316
|
-
|
|
566477
|
+
renderInfo2(
|
|
566478
|
+
" macOS ARM detected — installing build deps via Homebrew..."
|
|
566479
|
+
);
|
|
566480
|
+
const brewCheck = spawnSync7("which", ["brew"], {
|
|
566481
|
+
stdio: "pipe",
|
|
566482
|
+
timeout: 5e3
|
|
566483
|
+
});
|
|
566317
566484
|
if (brewCheck.status === 0) {
|
|
566318
|
-
const brewResult = spawnSync7(
|
|
566319
|
-
|
|
566320
|
-
|
|
566321
|
-
|
|
566485
|
+
const brewResult = spawnSync7(
|
|
566486
|
+
"brew",
|
|
566487
|
+
["install", "llvm", "gcc", "openblas", "libsndfile"],
|
|
566488
|
+
{
|
|
566489
|
+
stdio: "pipe",
|
|
566490
|
+
timeout: 3e5
|
|
566491
|
+
}
|
|
566492
|
+
);
|
|
566322
566493
|
if (brewResult.stdout) process.stdout.write(brewResult.stdout);
|
|
566323
566494
|
if (brewResult.stderr) {
|
|
566324
566495
|
const { renderVerbose: renderVerbose2 } = await Promise.resolve().then(() => (init_render2(), render_exports));
|
|
566325
566496
|
renderVerbose2(brewResult.stderr.toString());
|
|
566326
566497
|
}
|
|
566327
|
-
const llvmPrefix = spawnSync7("brew", ["--prefix", "llvm"], {
|
|
566498
|
+
const llvmPrefix = spawnSync7("brew", ["--prefix", "llvm"], {
|
|
566499
|
+
stdio: "pipe",
|
|
566500
|
+
timeout: 5e3
|
|
566501
|
+
});
|
|
566328
566502
|
if (llvmPrefix.stdout) {
|
|
566329
566503
|
const prefix = llvmPrefix.stdout.toString().trim();
|
|
566330
566504
|
process.env.LLVM_CONFIG = `${prefix}/bin/llvm-config`;
|
|
566331
|
-
const openblas = spawnSync7("brew", ["--prefix", "openblas"], {
|
|
566505
|
+
const openblas = spawnSync7("brew", ["--prefix", "openblas"], {
|
|
566506
|
+
stdio: "pipe",
|
|
566507
|
+
timeout: 5e3
|
|
566508
|
+
});
|
|
566332
566509
|
if (openblas.stdout) {
|
|
566333
566510
|
const obPrefix = openblas.stdout.toString().trim();
|
|
566334
566511
|
process.env.LDFLAGS = `${process.env.LDFLAGS ?? ""} -L${obPrefix}/lib`.trim();
|
|
@@ -566337,32 +566514,47 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566337
566514
|
}
|
|
566338
566515
|
} else {
|
|
566339
566516
|
renderWarning2(" Homebrew not found. Install it: https://brew.sh");
|
|
566340
|
-
renderWarning2(
|
|
566517
|
+
renderWarning2(
|
|
566518
|
+
" Then run: brew install llvm gcc openblas libsndfile"
|
|
566519
|
+
);
|
|
566341
566520
|
}
|
|
566342
566521
|
} else {
|
|
566343
|
-
renderInfo2(
|
|
566344
|
-
|
|
566522
|
+
renderInfo2(
|
|
566523
|
+
" System build tools needed (llvm, gcc). Requesting sudo access..."
|
|
566524
|
+
);
|
|
566525
|
+
const sudoCheck = spawnSync7("sudo", ["-v"], {
|
|
566526
|
+
stdio: "inherit",
|
|
566527
|
+
timeout: 6e4
|
|
566528
|
+
});
|
|
566345
566529
|
if (sudoCheck.status === 0) {
|
|
566346
|
-
renderInfo2(
|
|
566347
|
-
|
|
566348
|
-
|
|
566349
|
-
|
|
566350
|
-
"
|
|
566351
|
-
|
|
566352
|
-
|
|
566353
|
-
|
|
566354
|
-
|
|
566355
|
-
|
|
566356
|
-
|
|
566357
|
-
|
|
566358
|
-
|
|
566530
|
+
renderInfo2(
|
|
566531
|
+
" Installing system build dependencies (this may take a minute)..."
|
|
566532
|
+
);
|
|
566533
|
+
const aptResult = spawnSync7(
|
|
566534
|
+
"sudo",
|
|
566535
|
+
[
|
|
566536
|
+
"apt-get",
|
|
566537
|
+
"install",
|
|
566538
|
+
"-y",
|
|
566539
|
+
"--no-install-recommends",
|
|
566540
|
+
"llvm-dev",
|
|
566541
|
+
"gcc",
|
|
566542
|
+
"g++",
|
|
566543
|
+
"gfortran",
|
|
566544
|
+
"libopenblas-dev",
|
|
566545
|
+
"libsndfile1-dev"
|
|
566546
|
+
],
|
|
566547
|
+
{ stdio: "pipe", timeout: 12e4 }
|
|
566548
|
+
);
|
|
566359
566549
|
if (aptResult.stdout) process.stdout.write(aptResult.stdout);
|
|
566360
566550
|
if (aptResult.stderr) {
|
|
566361
566551
|
const { renderVerbose: renderVerbose2 } = await Promise.resolve().then(() => (init_render2(), render_exports));
|
|
566362
566552
|
renderVerbose2(aptResult.stderr.toString());
|
|
566363
566553
|
}
|
|
566364
566554
|
} else {
|
|
566365
|
-
renderWarning2(
|
|
566555
|
+
renderWarning2(
|
|
566556
|
+
" sudo not available — skipping system build deps. librosa/lhotse may fail to compile."
|
|
566557
|
+
);
|
|
566366
566558
|
}
|
|
566367
566559
|
}
|
|
566368
566560
|
if (process.stdout.isTTY) {
|
|
@@ -566372,14 +566564,20 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566372
566564
|
process.stdout.write("\x1B[?1002l\x1B[?1003l");
|
|
566373
566565
|
}
|
|
566374
566566
|
} catch (err) {
|
|
566375
|
-
renderWarning2(
|
|
566567
|
+
renderWarning2(
|
|
566568
|
+
` Could not install system build deps: ${err instanceof Error ? err.message : String(err)}`
|
|
566569
|
+
);
|
|
566376
566570
|
}
|
|
566377
566571
|
}
|
|
566378
566572
|
const isJetson = isArm && (existsSync87("/etc/nv_tegra_release") || existsSync87("/usr/local/cuda/targets/aarch64-linux") || (process.env.JETSON_L4T_VERSION ?? "") !== "");
|
|
566379
566573
|
const installSteps = isArm ? [
|
|
566380
566574
|
// ARM: install individually so we get clear error messages per package.
|
|
566381
566575
|
// ALL are fatal because LuxTTS hard-imports them (no lazy/optional imports).
|
|
566382
|
-
{
|
|
566576
|
+
{
|
|
566577
|
+
cmd: `${pipCmd} -m pip install --quiet "setuptools<81" wheel`,
|
|
566578
|
+
fatal: true,
|
|
566579
|
+
label: "setuptools"
|
|
566580
|
+
},
|
|
566383
566581
|
// Jetson: try NVIDIA's prebuilt PyTorch wheel for detected JetPack version.
|
|
566384
566582
|
// JetPack versions have different PyTorch wheel URLs:
|
|
566385
566583
|
// JP6.x: https://developer.download.nvidia.com/compute/redist/jp/v60/pytorch/
|
|
@@ -566388,46 +566586,119 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566388
566586
|
...isJetson ? (() => {
|
|
566389
566587
|
let jpVer = "v60";
|
|
566390
566588
|
try {
|
|
566391
|
-
const tegra = existsSync87("/etc/nv_tegra_release") ? execSync51("cat /etc/nv_tegra_release 2>/dev/null", {
|
|
566392
|
-
|
|
566589
|
+
const tegra = existsSync87("/etc/nv_tegra_release") ? execSync51("cat /etc/nv_tegra_release 2>/dev/null", {
|
|
566590
|
+
encoding: "utf8",
|
|
566591
|
+
timeout: 3e3
|
|
566592
|
+
}).trim() : "";
|
|
566593
|
+
const dpkg = execSync51(
|
|
566594
|
+
"dpkg -l nvidia-jetpack 2>/dev/null | grep nvidia-jetpack | awk '{print $3}'",
|
|
566595
|
+
{ encoding: "utf8", timeout: 5e3 }
|
|
566596
|
+
).trim();
|
|
566393
566597
|
const ver = dpkg || process.env.JETSON_L4T_VERSION || "";
|
|
566394
|
-
if (ver.startsWith("5.") || tegra.includes("R35") || tegra.includes("R34"))
|
|
566395
|
-
|
|
566396
|
-
else if (ver.startsWith("6.") || tegra.includes("R36"))
|
|
566598
|
+
if (ver.startsWith("5.") || tegra.includes("R35") || tegra.includes("R34"))
|
|
566599
|
+
jpVer = "v51";
|
|
566600
|
+
else if (ver.startsWith("6.1") || tegra.includes("R36.4"))
|
|
566601
|
+
jpVer = "v61";
|
|
566602
|
+
else if (ver.startsWith("6.") || tegra.includes("R36"))
|
|
566603
|
+
jpVer = "v60";
|
|
566397
566604
|
} catch {
|
|
566398
566605
|
}
|
|
566399
566606
|
return [
|
|
566400
|
-
{
|
|
566401
|
-
|
|
566607
|
+
{
|
|
566608
|
+
cmd: `${pipCmd} -m pip install --quiet torch torchvision torchaudio --index-url https://developer.download.nvidia.com/compute/redist/jp/${jpVer}/pytorch/ 2>/dev/null || ${pipCmd} -m pip install --quiet torch torchaudio`,
|
|
566609
|
+
fatal: true,
|
|
566610
|
+
label: `PyTorch (Jetson JP ${jpVer})`
|
|
566611
|
+
},
|
|
566612
|
+
{
|
|
566613
|
+
cmd: `${pipCmd} -m pip install --quiet onnxruntime-gpu 2>/dev/null || true`,
|
|
566614
|
+
fatal: false,
|
|
566615
|
+
label: "onnxruntime-gpu (Jetson, optional)"
|
|
566616
|
+
}
|
|
566402
566617
|
];
|
|
566403
566618
|
})() : [
|
|
566404
566619
|
// Non-Jetson ARM: use default PyPI (has aarch64 wheels).
|
|
566405
566620
|
// DO NOT use --index-url https://download.pytorch.org/whl/cpu — that index
|
|
566406
566621
|
// only has x86_64 wheels. ARM needs the standard PyPI torch package.
|
|
566407
|
-
{
|
|
566622
|
+
{
|
|
566623
|
+
cmd: `${pipCmd} -m pip install --quiet torch torchaudio`,
|
|
566624
|
+
fatal: true,
|
|
566625
|
+
label: "PyTorch (ARM aarch64 from PyPI)"
|
|
566626
|
+
}
|
|
566408
566627
|
],
|
|
566409
|
-
{
|
|
566410
|
-
|
|
566411
|
-
|
|
566412
|
-
|
|
566628
|
+
{
|
|
566629
|
+
cmd: `${pipCmd} -m pip install --quiet numpy`,
|
|
566630
|
+
fatal: true,
|
|
566631
|
+
label: "numpy"
|
|
566632
|
+
},
|
|
566633
|
+
{
|
|
566634
|
+
cmd: `${pipCmd} -m pip install --quiet huggingface_hub safetensors`,
|
|
566635
|
+
fatal: true,
|
|
566636
|
+
label: "huggingface_hub + safetensors"
|
|
566637
|
+
},
|
|
566638
|
+
{
|
|
566639
|
+
cmd: `${pipCmd} -m pip install --quiet "transformers<=4.57.6"`,
|
|
566640
|
+
fatal: true,
|
|
566641
|
+
label: "transformers"
|
|
566642
|
+
},
|
|
566643
|
+
{
|
|
566644
|
+
cmd: `${pipCmd} -m pip install --quiet pydub inflect`,
|
|
566645
|
+
fatal: true,
|
|
566646
|
+
label: "pydub + inflect"
|
|
566647
|
+
},
|
|
566413
566648
|
// llvmlite (needed by numba, needed by librosa): try prebuilt first, then compile
|
|
566414
|
-
{
|
|
566415
|
-
|
|
566649
|
+
{
|
|
566650
|
+
cmd: `${pipCmd} -m pip install --quiet llvmlite 2>/dev/null || LLVM_CONFIG=$(which llvm-config || which llvm-config-14 || echo llvm-config) ${pipCmd} -m pip install --quiet llvmlite`,
|
|
566651
|
+
fatal: false,
|
|
566652
|
+
label: "llvmlite (ARM — trying prebuilt, then compiling)"
|
|
566653
|
+
},
|
|
566654
|
+
{
|
|
566655
|
+
cmd: `${pipCmd} -m pip install --quiet numba`,
|
|
566656
|
+
fatal: false,
|
|
566657
|
+
label: "numba"
|
|
566658
|
+
},
|
|
566416
566659
|
// librosa: if numba/llvmlite failed, librosa degrades but still works for basic audio loading
|
|
566417
|
-
{
|
|
566418
|
-
|
|
566660
|
+
{
|
|
566661
|
+
cmd: `${pipCmd} -m pip install --quiet librosa`,
|
|
566662
|
+
fatal: true,
|
|
566663
|
+
label: "librosa"
|
|
566664
|
+
},
|
|
566665
|
+
{
|
|
566666
|
+
cmd: `${pipCmd} -m pip install --quiet lhotse`,
|
|
566667
|
+
fatal: true,
|
|
566668
|
+
label: "lhotse"
|
|
566669
|
+
},
|
|
566419
566670
|
// vocos: try pip, fallback to building from source if wheels missing
|
|
566420
|
-
{
|
|
566671
|
+
{
|
|
566672
|
+
cmd: `${pipCmd} -m pip install --quiet vocos 2>/dev/null || ${pipCmd} -m pip install --quiet --no-build-isolation vocos`,
|
|
566673
|
+
fatal: true,
|
|
566674
|
+
label: "vocos"
|
|
566675
|
+
},
|
|
566421
566676
|
// LinaCodec: needs C++ build tools on ARM. Install with --no-build-isolation
|
|
566422
566677
|
// and fallback to CPU-only if CUDA compilation fails.
|
|
566423
|
-
{
|
|
566678
|
+
{
|
|
566679
|
+
cmd: `${pipCmd} -m pip install --quiet "git+https://github.com/ysharma3501/LinaCodec.git" 2>/dev/null || ${pipCmd} -m pip install --quiet --no-build-isolation "git+https://github.com/ysharma3501/LinaCodec.git"`,
|
|
566680
|
+
fatal: true,
|
|
566681
|
+
label: "LinaCodec (voice cloning codec)"
|
|
566682
|
+
},
|
|
566424
566683
|
// Non-fatal (not hard-imported by LuxTTS):
|
|
566425
|
-
{
|
|
566426
|
-
|
|
566684
|
+
{
|
|
566685
|
+
cmd: `${pipCmd} -m pip install --quiet piper-phonemize --find-links https://k2-fsa.github.io/icefall/piper_phonemize.html`,
|
|
566686
|
+
fatal: false,
|
|
566687
|
+
label: "piper-phonemize (optional)"
|
|
566688
|
+
},
|
|
566689
|
+
{
|
|
566690
|
+
cmd: `${pipCmd} -m pip install --quiet jieba pypinyin cn2an`,
|
|
566691
|
+
fatal: true,
|
|
566692
|
+
label: "Chinese text processing"
|
|
566693
|
+
},
|
|
566427
566694
|
// LuxTTS itself: use --no-deps on ARM because we installed deps individually above.
|
|
566428
566695
|
// Without --no-deps, pip tries to resolve zipvoice's full dependency tree which
|
|
566429
566696
|
// includes piper-phonemize — and that has no macOS ARM wheel, causing fatal failure.
|
|
566430
|
-
{
|
|
566697
|
+
{
|
|
566698
|
+
cmd: `${pipCmd} -m pip install --quiet --no-deps -e ${JSON.stringify(repoDir)}`,
|
|
566699
|
+
fatal: true,
|
|
566700
|
+
label: "LuxTTS (editable install)"
|
|
566701
|
+
}
|
|
566431
566702
|
] : [
|
|
566432
566703
|
// x86_64: all-in-one (fast, all wheels available)
|
|
566433
566704
|
{
|
|
@@ -566435,10 +566706,26 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566435
566706
|
fatal: true,
|
|
566436
566707
|
label: "core LuxTTS deps"
|
|
566437
566708
|
},
|
|
566438
|
-
{
|
|
566439
|
-
|
|
566440
|
-
|
|
566441
|
-
|
|
566709
|
+
{
|
|
566710
|
+
cmd: `${pipCmd} -m pip install --quiet piper-phonemize --find-links https://k2-fsa.github.io/icefall/piper_phonemize.html`,
|
|
566711
|
+
fatal: false,
|
|
566712
|
+
label: "piper-phonemize"
|
|
566713
|
+
},
|
|
566714
|
+
{
|
|
566715
|
+
cmd: `${pipCmd} -m pip install --quiet jieba pypinyin cn2an`,
|
|
566716
|
+
fatal: true,
|
|
566717
|
+
label: "Chinese text processing"
|
|
566718
|
+
},
|
|
566719
|
+
{
|
|
566720
|
+
cmd: `${pipCmd} -m pip install --quiet "git+https://github.com/ysharma3501/LinaCodec.git"`,
|
|
566721
|
+
fatal: false,
|
|
566722
|
+
label: "LinaCodec"
|
|
566723
|
+
},
|
|
566724
|
+
{
|
|
566725
|
+
cmd: `${pipCmd} -m pip install --quiet -e ${JSON.stringify(repoDir)}`,
|
|
566726
|
+
fatal: true,
|
|
566727
|
+
label: "LuxTTS (editable install)"
|
|
566728
|
+
}
|
|
566442
566729
|
];
|
|
566443
566730
|
for (const step of installSteps) {
|
|
566444
566731
|
try {
|
|
@@ -566448,7 +566735,9 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566448
566735
|
if (!step.fatal) {
|
|
566449
566736
|
renderWarning2(` Skipped (${step.label}): ${msg.slice(0, 150)}`);
|
|
566450
566737
|
} else {
|
|
566451
|
-
throw new Error(
|
|
566738
|
+
throw new Error(
|
|
566739
|
+
`Failed to install LuxTTS dependencies (${step.label}): ${msg}`
|
|
566740
|
+
);
|
|
566452
566741
|
}
|
|
566453
566742
|
}
|
|
566454
566743
|
}
|
|
@@ -566471,7 +566760,12 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
|
566471
566760
|
if (this.luxttsCloneRef && existsSync87(this.luxttsCloneRef)) return;
|
|
566472
566761
|
const refsDir = luxttsCloneRefsDir();
|
|
566473
566762
|
if (!existsSync87(refsDir)) return;
|
|
566474
|
-
for (const name10 of [
|
|
566763
|
+
for (const name10 of [
|
|
566764
|
+
"custom-clone.wav",
|
|
566765
|
+
"custom-clone.mp3",
|
|
566766
|
+
"glados-ref.wav",
|
|
566767
|
+
"overwatch-ref.wav"
|
|
566768
|
+
]) {
|
|
566475
566769
|
const p2 = join104(refsDir, name10);
|
|
566476
566770
|
if (existsSync87(p2)) {
|
|
566477
566771
|
this.luxttsCloneRef = p2;
|
|
@@ -566674,7 +566968,10 @@ if __name__ == '__main__':
|
|
|
566674
566968
|
if (!cleaned) return null;
|
|
566675
566969
|
const ready = await this.ensureLuxttsDaemon();
|
|
566676
566970
|
if (!ready) return null;
|
|
566677
|
-
const wavPath = join104(
|
|
566971
|
+
const wavPath = join104(
|
|
566972
|
+
tmpdir20(),
|
|
566973
|
+
`oa-luxtts-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.wav`
|
|
566974
|
+
);
|
|
566678
566975
|
try {
|
|
566679
566976
|
await this.luxttsRequest({
|
|
566680
566977
|
action: "synthesize",
|
|
@@ -566703,7 +567000,10 @@ if __name__ == '__main__':
|
|
|
566703
567000
|
wavData.byteOffset + 44,
|
|
566704
567001
|
(wavData.length - 44) / 2
|
|
566705
567002
|
);
|
|
566706
|
-
const fadeInSamples = Math.min(
|
|
567003
|
+
const fadeInSamples = Math.min(
|
|
567004
|
+
Math.round(sampleRate * 0.35),
|
|
567005
|
+
samples.length
|
|
567006
|
+
);
|
|
566707
567007
|
for (let i2 = 0; i2 < fadeInSamples; i2++) {
|
|
566708
567008
|
samples[i2] = Math.round(samples[i2] * (i2 / fadeInSamples));
|
|
566709
567009
|
}
|
|
@@ -566713,7 +567013,10 @@ if __name__ == '__main__':
|
|
|
566713
567013
|
}
|
|
566714
567014
|
}
|
|
566715
567015
|
const header = wavData.subarray(0, 44);
|
|
566716
|
-
const scaled = Buffer.concat([
|
|
567016
|
+
const scaled = Buffer.concat([
|
|
567017
|
+
header,
|
|
567018
|
+
Buffer.from(samples.buffer, samples.byteOffset, samples.byteLength)
|
|
567019
|
+
]);
|
|
566717
567020
|
writeFileSync46(wavPath, scaled);
|
|
566718
567021
|
}
|
|
566719
567022
|
} catch {
|
|
@@ -566741,7 +567044,11 @@ if __name__ == '__main__':
|
|
|
566741
567044
|
try {
|
|
566742
567045
|
const wavData = readFileSync71(wavPath);
|
|
566743
567046
|
if (wavData.length > 44) {
|
|
566744
|
-
const pcm = Buffer.from(
|
|
567047
|
+
const pcm = Buffer.from(
|
|
567048
|
+
wavData.buffer,
|
|
567049
|
+
wavData.byteOffset + 44,
|
|
567050
|
+
wavData.length - 44
|
|
567051
|
+
);
|
|
566745
567052
|
const sampleRate = wavData.readUInt32LE(24);
|
|
566746
567053
|
this.onPCMOutput(pcm, sampleRate);
|
|
566747
567054
|
}
|
|
@@ -566764,7 +567071,11 @@ if __name__ == '__main__':
|
|
|
566764
567071
|
for (let i2 = 0; i2 < int16.length; i2++) {
|
|
566765
567072
|
float32[i2] = int16[i2] / 32768;
|
|
566766
567073
|
}
|
|
566767
|
-
const stereo = this.applyStereoDelay(
|
|
567074
|
+
const stereo = this.applyStereoDelay(
|
|
567075
|
+
float32,
|
|
567076
|
+
sampleRate,
|
|
567077
|
+
stereoDelayMs
|
|
567078
|
+
);
|
|
566768
567079
|
this.writeStereoWav(stereo.left, stereo.right, sampleRate, wavPath);
|
|
566769
567080
|
}
|
|
566770
567081
|
}
|
|
@@ -566782,7 +567093,12 @@ if __name__ == '__main__':
|
|
|
566782
567093
|
async synthesizeWithLuxtts(text, volume = 1, pitchFactor = 1, speedFactor = 1, stereoDelayMs = 0.6) {
|
|
566783
567094
|
const wavPath = await this.synthesizeLuxttsWav(text, speedFactor);
|
|
566784
567095
|
if (!wavPath) return;
|
|
566785
|
-
await this.postProcessAndPlayLuxtts(
|
|
567096
|
+
await this.postProcessAndPlayLuxtts(
|
|
567097
|
+
wavPath,
|
|
567098
|
+
volume,
|
|
567099
|
+
pitchFactor,
|
|
567100
|
+
stereoDelayMs
|
|
567101
|
+
);
|
|
566786
567102
|
}
|
|
566787
567103
|
/**
|
|
566788
567104
|
* Synthesize text to WAV buffer using LuxTTS (no playback).
|
|
@@ -566825,7 +567141,7 @@ if __name__ == '__main__':
|
|
|
566825
567141
|
const pkgPath = join104(voiceDir(), "package.json");
|
|
566826
567142
|
const expectedDeps = {
|
|
566827
567143
|
"onnxruntime-node": "^1.21.0",
|
|
566828
|
-
|
|
567144
|
+
phonemizer: "^1.2.1"
|
|
566829
567145
|
};
|
|
566830
567146
|
if (existsSync87(pkgPath)) {
|
|
566831
567147
|
try {
|
|
@@ -566838,11 +567154,18 @@ if __name__ == '__main__':
|
|
|
566838
567154
|
}
|
|
566839
567155
|
}
|
|
566840
567156
|
if (!existsSync87(pkgPath)) {
|
|
566841
|
-
writeFileSync46(
|
|
566842
|
-
|
|
566843
|
-
|
|
566844
|
-
|
|
566845
|
-
|
|
567157
|
+
writeFileSync46(
|
|
567158
|
+
pkgPath,
|
|
567159
|
+
JSON.stringify(
|
|
567160
|
+
{
|
|
567161
|
+
name: "open-agents-voice",
|
|
567162
|
+
private: true,
|
|
567163
|
+
dependencies: expectedDeps
|
|
567164
|
+
},
|
|
567165
|
+
null,
|
|
567166
|
+
2
|
|
567167
|
+
)
|
|
567168
|
+
);
|
|
566846
567169
|
}
|
|
566847
567170
|
const voiceRequire = createRequire4(join104(voiceDir(), "index.js"));
|
|
566848
567171
|
const probeOnnx = async () => {
|
|
@@ -566856,7 +567179,11 @@ if __name__ == '__main__':
|
|
|
566856
567179
|
return false;
|
|
566857
567180
|
}
|
|
566858
567181
|
};
|
|
566859
|
-
const onnxNodeModules = join104(
|
|
567182
|
+
const onnxNodeModules = join104(
|
|
567183
|
+
voiceDir(),
|
|
567184
|
+
"node_modules",
|
|
567185
|
+
"onnxruntime-node"
|
|
567186
|
+
);
|
|
566860
567187
|
const onnxInstalled = existsSync87(onnxNodeModules);
|
|
566861
567188
|
if (onnxInstalled && !await probeOnnx()) {
|
|
566862
567189
|
throw new Error(
|
|
@@ -566868,7 +567195,10 @@ if __name__ == '__main__':
|
|
|
566868
567195
|
} catch {
|
|
566869
567196
|
renderInfo2("Installing ONNX runtime for voice synthesis (background)...");
|
|
566870
567197
|
try {
|
|
566871
|
-
await this.asyncShell(
|
|
567198
|
+
await this.asyncShell(
|
|
567199
|
+
`cd "${voiceDir()}" && npm install --no-audit --no-fund`,
|
|
567200
|
+
12e4
|
|
567201
|
+
);
|
|
566872
567202
|
} catch (err) {
|
|
566873
567203
|
const archHint = arch3 !== "x64" ? ` onnxruntime-node may not have prebuilt binaries for ${process.platform}-${arch3}.` : "";
|
|
566874
567204
|
throw new Error(
|
|
@@ -566895,7 +567225,10 @@ Error: ${err instanceof Error ? err.message : String(err)}`
|
|
|
566895
567225
|
} catch {
|
|
566896
567226
|
renderInfo2("Installing phonemizer for voice synthesis (background)...");
|
|
566897
567227
|
try {
|
|
566898
|
-
await this.asyncShell(
|
|
567228
|
+
await this.asyncShell(
|
|
567229
|
+
`cd "${voiceDir()}" && npm install --no-audit --no-fund`,
|
|
567230
|
+
12e4
|
|
567231
|
+
);
|
|
566899
567232
|
const phonemizerMod = voiceRequire("phonemizer");
|
|
566900
567233
|
this.phonemizeFn = phonemizerMod.phonemize ?? phonemizerMod.default?.phonemize ?? phonemizerMod;
|
|
566901
567234
|
} catch (err) {
|
|
@@ -566921,17 +567254,24 @@ Error: ${err instanceof Error ? err.message : String(err)}`
|
|
|
566921
567254
|
if (!existsSync87(configPath2)) {
|
|
566922
567255
|
renderInfo2(`Downloading ${model.label} voice config...`);
|
|
566923
567256
|
const configResp = await fetch(model.configUrl);
|
|
566924
|
-
if (!configResp.ok)
|
|
567257
|
+
if (!configResp.ok)
|
|
567258
|
+
throw new Error(`Failed to download config: HTTP ${configResp.status}`);
|
|
566925
567259
|
const configText = await configResp.text();
|
|
566926
567260
|
writeFileSync46(configPath2, configText);
|
|
566927
567261
|
}
|
|
566928
567262
|
if (!existsSync87(onnxPath)) {
|
|
566929
|
-
renderInfo2(
|
|
567263
|
+
renderInfo2(
|
|
567264
|
+
`Downloading ${model.label} voice model (this may take a minute)...`
|
|
567265
|
+
);
|
|
566930
567266
|
const onnxResp = await fetch(model.onnxUrl);
|
|
566931
|
-
if (!onnxResp.ok)
|
|
567267
|
+
if (!onnxResp.ok)
|
|
567268
|
+
throw new Error(`Failed to download model: HTTP ${onnxResp.status}`);
|
|
566932
567269
|
const reader = onnxResp.body?.getReader();
|
|
566933
567270
|
if (!reader) throw new Error("No response body");
|
|
566934
|
-
const contentLength = parseInt(
|
|
567271
|
+
const contentLength = parseInt(
|
|
567272
|
+
onnxResp.headers.get("content-length") || "0",
|
|
567273
|
+
10
|
|
567274
|
+
);
|
|
566935
567275
|
const chunks = [];
|
|
566936
567276
|
let received = 0;
|
|
566937
567277
|
while (true) {
|
|
@@ -566942,13 +567282,17 @@ Error: ${err instanceof Error ? err.message : String(err)}`
|
|
|
566942
567282
|
if (contentLength > 0) {
|
|
566943
567283
|
const pct = Math.round(received / contentLength * 100);
|
|
566944
567284
|
if (pct === 25 || pct === 50 || pct === 75 || pct === 100) {
|
|
566945
|
-
renderInfo2(
|
|
567285
|
+
renderInfo2(
|
|
567286
|
+
` ${pct}% (${formatBytes2(received)} / ${formatBytes2(contentLength)})`
|
|
567287
|
+
);
|
|
566946
567288
|
}
|
|
566947
567289
|
}
|
|
566948
567290
|
}
|
|
566949
567291
|
const fullBuffer = Buffer.concat(chunks);
|
|
566950
567292
|
writeFileSync46(onnxPath, fullBuffer);
|
|
566951
|
-
renderInfo2(
|
|
567293
|
+
renderInfo2(
|
|
567294
|
+
`${model.label} model downloaded (${formatBytes2(fullBuffer.length)}).`
|
|
567295
|
+
);
|
|
566952
567296
|
}
|
|
566953
567297
|
}
|
|
566954
567298
|
// -------------------------------------------------------------------------
|