open-agents-ai 0.103.80 → 0.103.81
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 +187 -75
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -38957,6 +38957,19 @@ var init_voice = __esm({
|
|
|
38957
38957
|
this.config = null;
|
|
38958
38958
|
this.mlxActive = false;
|
|
38959
38959
|
this.luxttsActive = false;
|
|
38960
|
+
if (this._luxttsDaemon && !this._luxttsDaemon.killed) {
|
|
38961
|
+
try {
|
|
38962
|
+
this._luxttsDaemon.stdin.write('{"action":"quit"}\n');
|
|
38963
|
+
} catch {
|
|
38964
|
+
}
|
|
38965
|
+
setTimeout(() => {
|
|
38966
|
+
try {
|
|
38967
|
+
this._luxttsDaemon?.kill();
|
|
38968
|
+
} catch {
|
|
38969
|
+
}
|
|
38970
|
+
}, 2e3);
|
|
38971
|
+
}
|
|
38972
|
+
this._luxttsDaemon = null;
|
|
38960
38973
|
}
|
|
38961
38974
|
// -------------------------------------------------------------------------
|
|
38962
38975
|
// Text chunking for long TTS input
|
|
@@ -39610,70 +39623,95 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
|
39610
39623
|
}
|
|
39611
39624
|
}
|
|
39612
39625
|
}
|
|
39613
|
-
/**
|
|
39626
|
+
/** Persistent LuxTTS daemon process — keeps model warm in GPU memory */
|
|
39627
|
+
_luxttsDaemon = null;
|
|
39628
|
+
_luxttsPending = /* @__PURE__ */ new Map();
|
|
39629
|
+
_luxttsBuffer = "";
|
|
39630
|
+
/** Write the LuxTTS persistent daemon Python script */
|
|
39614
39631
|
writeLuxttsInferScript() {
|
|
39615
39632
|
const script = `#!/usr/bin/env python3
|
|
39616
|
-
"""LuxTTS
|
|
39617
|
-
|
|
39633
|
+
"""LuxTTS persistent daemon for open-agents voice engine.
|
|
39634
|
+
Keeps model warm in GPU memory. Reads JSON requests from stdin, writes JSON responses to stdout.
|
|
39618
39635
|
"""
|
|
39619
|
-
import sys, json, os, wave,
|
|
39636
|
+
import sys, json, os, wave, signal
|
|
39620
39637
|
import numpy as np
|
|
39621
39638
|
|
|
39622
39639
|
def main():
|
|
39623
|
-
|
|
39624
|
-
action = args.get('action', 'synthesize')
|
|
39625
|
-
|
|
39626
|
-
repo_path = args.get('repo_path', '')
|
|
39640
|
+
repo_path = os.environ.get('LUXTTS_REPO_PATH', '')
|
|
39627
39641
|
if repo_path:
|
|
39628
39642
|
sys.path.insert(0, repo_path)
|
|
39629
39643
|
|
|
39630
|
-
|
|
39644
|
+
import torch
|
|
39645
|
+
from zipvoice.luxvoice import LuxTTS
|
|
39646
|
+
|
|
39647
|
+
# Auto-detect best device
|
|
39648
|
+
if torch.cuda.is_available():
|
|
39649
|
+
device = 'cuda'
|
|
39650
|
+
elif hasattr(torch.backends, 'mps') and torch.backends.mps.is_available():
|
|
39651
|
+
device = 'mps'
|
|
39652
|
+
else:
|
|
39653
|
+
device = 'cpu'
|
|
39654
|
+
|
|
39655
|
+
# Load model ONCE \u2014 stays warm in GPU/CPU memory
|
|
39656
|
+
tts = LuxTTS(model_path='YatharthS/LuxTTS', device=device, threads=4)
|
|
39657
|
+
sys.stdout.write(json.dumps({'type': 'ready', 'device': device}) + '\\n')
|
|
39658
|
+
sys.stdout.flush()
|
|
39659
|
+
|
|
39660
|
+
# Cache encoded prompts by clone_ref path (avoid re-encoding same voice)
|
|
39661
|
+
prompt_cache = {}
|
|
39662
|
+
|
|
39663
|
+
for line in sys.stdin:
|
|
39664
|
+
line = line.strip()
|
|
39665
|
+
if not line:
|
|
39666
|
+
continue
|
|
39631
39667
|
try:
|
|
39632
|
-
|
|
39633
|
-
print(json.dumps({'ok': True}))
|
|
39668
|
+
req = json.loads(line)
|
|
39634
39669
|
except Exception as e:
|
|
39635
|
-
|
|
39636
|
-
|
|
39637
|
-
|
|
39638
|
-
|
|
39639
|
-
|
|
39640
|
-
|
|
39641
|
-
|
|
39642
|
-
|
|
39643
|
-
|
|
39644
|
-
|
|
39645
|
-
|
|
39646
|
-
|
|
39647
|
-
|
|
39648
|
-
|
|
39649
|
-
|
|
39650
|
-
|
|
39651
|
-
|
|
39652
|
-
|
|
39653
|
-
|
|
39654
|
-
|
|
39655
|
-
|
|
39656
|
-
|
|
39657
|
-
|
|
39658
|
-
|
|
39659
|
-
|
|
39660
|
-
|
|
39661
|
-
|
|
39662
|
-
|
|
39663
|
-
|
|
39664
|
-
|
|
39665
|
-
|
|
39666
|
-
|
|
39667
|
-
|
|
39668
|
-
|
|
39669
|
-
|
|
39670
|
-
|
|
39671
|
-
|
|
39672
|
-
|
|
39673
|
-
|
|
39674
|
-
|
|
39675
|
-
|
|
39676
|
-
|
|
39670
|
+
sys.stdout.write(json.dumps({'type': 'error', 'id': '', 'error': str(e)}) + '\\n')
|
|
39671
|
+
sys.stdout.flush()
|
|
39672
|
+
continue
|
|
39673
|
+
|
|
39674
|
+
req_id = req.get('id', '')
|
|
39675
|
+
|
|
39676
|
+
if req.get('action') == 'check':
|
|
39677
|
+
sys.stdout.write(json.dumps({'type': 'result', 'id': req_id, 'ok': True}) + '\\n')
|
|
39678
|
+
sys.stdout.flush()
|
|
39679
|
+
continue
|
|
39680
|
+
|
|
39681
|
+
if req.get('action') == 'quit':
|
|
39682
|
+
break
|
|
39683
|
+
|
|
39684
|
+
if req.get('action') == 'synthesize':
|
|
39685
|
+
try:
|
|
39686
|
+
text = req['text']
|
|
39687
|
+
clone_ref = req['clone_ref']
|
|
39688
|
+
output_path = req['output_path']
|
|
39689
|
+
speed = req.get('speed', 1.0)
|
|
39690
|
+
|
|
39691
|
+
# Use cached prompt encoding if available
|
|
39692
|
+
if clone_ref not in prompt_cache:
|
|
39693
|
+
prompt_cache[clone_ref] = tts.encode_prompt(clone_ref, duration=5, rms=0.001)
|
|
39694
|
+
encoded = prompt_cache[clone_ref]
|
|
39695
|
+
|
|
39696
|
+
wav_tensor = tts.generate_speech(text, encoded, num_steps=4, guidance_scale=3.0, t_shift=0.5, speed=speed)
|
|
39697
|
+
wav_np = wav_tensor.cpu().numpy().squeeze()
|
|
39698
|
+
|
|
39699
|
+
int16_data = (np.clip(wav_np, -1.0, 1.0) * 32767).astype(np.int16)
|
|
39700
|
+
with wave.open(output_path, 'wb') as wf:
|
|
39701
|
+
wf.setnchannels(1)
|
|
39702
|
+
wf.setsampwidth(2)
|
|
39703
|
+
wf.setframerate(48000)
|
|
39704
|
+
wf.writeframes(int16_data.tobytes())
|
|
39705
|
+
|
|
39706
|
+
sys.stdout.write(json.dumps({
|
|
39707
|
+
'type': 'result', 'id': req_id, 'ok': True,
|
|
39708
|
+
'path': output_path, 'sample_rate': 48000,
|
|
39709
|
+
'device': device, 'samples': len(int16_data),
|
|
39710
|
+
}) + '\\n')
|
|
39711
|
+
sys.stdout.flush()
|
|
39712
|
+
except Exception as e:
|
|
39713
|
+
sys.stdout.write(json.dumps({'type': 'error', 'id': req_id, 'error': str(e)}) + '\\n')
|
|
39714
|
+
sys.stdout.flush()
|
|
39677
39715
|
|
|
39678
39716
|
if __name__ == '__main__':
|
|
39679
39717
|
main()
|
|
@@ -39682,6 +39720,84 @@ if __name__ == '__main__':
|
|
|
39682
39720
|
mkdirSync14(voiceDir(), { recursive: true });
|
|
39683
39721
|
writeFileSync14(scriptPath2, script);
|
|
39684
39722
|
}
|
|
39723
|
+
/** Ensure the LuxTTS daemon is running, spawn if needed */
|
|
39724
|
+
async ensureLuxttsDaemon() {
|
|
39725
|
+
if (this._luxttsDaemon && !this._luxttsDaemon.killed)
|
|
39726
|
+
return true;
|
|
39727
|
+
const venvPy = luxttsVenvPy();
|
|
39728
|
+
if (!existsSync34(venvPy))
|
|
39729
|
+
return false;
|
|
39730
|
+
return new Promise((resolve31) => {
|
|
39731
|
+
const env = { ...process.env, LUXTTS_REPO_PATH: luxttsRepoDir() };
|
|
39732
|
+
const daemon = nodeSpawn(venvPy, [luxttsInferScript()], {
|
|
39733
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
39734
|
+
cwd: tmpdir6(),
|
|
39735
|
+
env
|
|
39736
|
+
});
|
|
39737
|
+
this._luxttsDaemon = daemon;
|
|
39738
|
+
this._luxttsBuffer = "";
|
|
39739
|
+
daemon.stdout.on("data", (chunk) => {
|
|
39740
|
+
this._luxttsBuffer += chunk.toString();
|
|
39741
|
+
const lines = this._luxttsBuffer.split("\n");
|
|
39742
|
+
this._luxttsBuffer = lines.pop();
|
|
39743
|
+
for (const line of lines) {
|
|
39744
|
+
if (!line.trim())
|
|
39745
|
+
continue;
|
|
39746
|
+
try {
|
|
39747
|
+
const msg = JSON.parse(line);
|
|
39748
|
+
if (msg.type === "ready") {
|
|
39749
|
+
resolve31(true);
|
|
39750
|
+
} else if (msg.type === "result" || msg.type === "error") {
|
|
39751
|
+
const pending = this._luxttsPending.get(msg.id);
|
|
39752
|
+
if (pending) {
|
|
39753
|
+
this._luxttsPending.delete(msg.id);
|
|
39754
|
+
if (msg.type === "error")
|
|
39755
|
+
pending.reject(new Error(msg.error));
|
|
39756
|
+
else
|
|
39757
|
+
pending.resolve(line);
|
|
39758
|
+
}
|
|
39759
|
+
}
|
|
39760
|
+
} catch {
|
|
39761
|
+
}
|
|
39762
|
+
}
|
|
39763
|
+
});
|
|
39764
|
+
daemon.on("exit", () => {
|
|
39765
|
+
this._luxttsDaemon = null;
|
|
39766
|
+
for (const [, p] of this._luxttsPending) {
|
|
39767
|
+
p.reject(new Error("LuxTTS daemon exited"));
|
|
39768
|
+
}
|
|
39769
|
+
this._luxttsPending.clear();
|
|
39770
|
+
});
|
|
39771
|
+
daemon.on("error", () => {
|
|
39772
|
+
this._luxttsDaemon = null;
|
|
39773
|
+
resolve31(false);
|
|
39774
|
+
});
|
|
39775
|
+
setTimeout(() => {
|
|
39776
|
+
if (this._luxttsDaemon === daemon && !this._luxttsPending.has("__ready__")) {
|
|
39777
|
+
resolve31(false);
|
|
39778
|
+
}
|
|
39779
|
+
}, 6e4);
|
|
39780
|
+
});
|
|
39781
|
+
}
|
|
39782
|
+
/** Send a request to the LuxTTS daemon and await the response */
|
|
39783
|
+
luxttsRequest(req) {
|
|
39784
|
+
return new Promise((resolve31, reject) => {
|
|
39785
|
+
if (!this._luxttsDaemon || this._luxttsDaemon.killed) {
|
|
39786
|
+
reject(new Error("LuxTTS daemon not running"));
|
|
39787
|
+
return;
|
|
39788
|
+
}
|
|
39789
|
+
const id = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
39790
|
+
req.id = id;
|
|
39791
|
+
this._luxttsPending.set(id, { resolve: resolve31, reject });
|
|
39792
|
+
this._luxttsDaemon.stdin.write(JSON.stringify(req) + "\n");
|
|
39793
|
+
setTimeout(() => {
|
|
39794
|
+
if (this._luxttsPending.has(id)) {
|
|
39795
|
+
this._luxttsPending.delete(id);
|
|
39796
|
+
reject(new Error("LuxTTS synthesis timeout"));
|
|
39797
|
+
}
|
|
39798
|
+
}, 12e4);
|
|
39799
|
+
});
|
|
39800
|
+
}
|
|
39685
39801
|
/**
|
|
39686
39802
|
* Synthesize and play audio using LuxTTS voice cloning.
|
|
39687
39803
|
* Speed is passed natively to LuxTTS. Pitch is post-processed via resampling.
|
|
@@ -39694,20 +39810,18 @@ if __name__ == '__main__':
|
|
|
39694
39810
|
const cleaned = text.replace(/\*/g, "").trim();
|
|
39695
39811
|
if (!cleaned)
|
|
39696
39812
|
return;
|
|
39697
|
-
const
|
|
39698
|
-
if (!
|
|
39813
|
+
const ready = await this.ensureLuxttsDaemon();
|
|
39814
|
+
if (!ready)
|
|
39699
39815
|
return;
|
|
39700
39816
|
const wavPath = join43(tmpdir6(), `oa-luxtts-${Date.now()}.wav`);
|
|
39701
|
-
const inferArgs = JSON.stringify({
|
|
39702
|
-
action: "synthesize",
|
|
39703
|
-
repo_path: luxttsRepoDir(),
|
|
39704
|
-
text: cleaned,
|
|
39705
|
-
clone_ref: this.luxttsCloneRef,
|
|
39706
|
-
output_path: wavPath,
|
|
39707
|
-
speed: speedFactor
|
|
39708
|
-
});
|
|
39709
39817
|
try {
|
|
39710
|
-
|
|
39818
|
+
await this.luxttsRequest({
|
|
39819
|
+
action: "synthesize",
|
|
39820
|
+
text: cleaned,
|
|
39821
|
+
clone_ref: this.luxttsCloneRef,
|
|
39822
|
+
output_path: wavPath,
|
|
39823
|
+
speed: speedFactor
|
|
39824
|
+
});
|
|
39711
39825
|
} catch {
|
|
39712
39826
|
return;
|
|
39713
39827
|
}
|
|
@@ -39770,20 +39884,18 @@ if __name__ == '__main__':
|
|
|
39770
39884
|
const cleaned = text.replace(/\*/g, "").trim();
|
|
39771
39885
|
if (!cleaned)
|
|
39772
39886
|
return null;
|
|
39773
|
-
const
|
|
39774
|
-
if (!
|
|
39887
|
+
const ready = await this.ensureLuxttsDaemon();
|
|
39888
|
+
if (!ready)
|
|
39775
39889
|
return null;
|
|
39776
39890
|
const wavPath = join43(tmpdir6(), `oa-luxtts-buf-${Date.now()}.wav`);
|
|
39777
|
-
const inferArgs = JSON.stringify({
|
|
39778
|
-
action: "synthesize",
|
|
39779
|
-
repo_path: luxttsRepoDir(),
|
|
39780
|
-
text: cleaned,
|
|
39781
|
-
clone_ref: this.luxttsCloneRef,
|
|
39782
|
-
output_path: wavPath,
|
|
39783
|
-
speed: 1
|
|
39784
|
-
});
|
|
39785
39891
|
try {
|
|
39786
|
-
|
|
39892
|
+
await this.luxttsRequest({
|
|
39893
|
+
action: "synthesize",
|
|
39894
|
+
text: cleaned,
|
|
39895
|
+
clone_ref: this.luxttsCloneRef,
|
|
39896
|
+
output_path: wavPath,
|
|
39897
|
+
speed: 1
|
|
39898
|
+
});
|
|
39787
39899
|
} catch {
|
|
39788
39900
|
return null;
|
|
39789
39901
|
}
|
package/package.json
CHANGED