feat(voice): M47 后端启动后探活 emotionTTS,未起则异步拉起一键启动脚本
主人 2026-10-24:「tts启动脚本在这 ...\一键启动.bat,以后启动后端可以顺便检测 一下如果TTS没起就顺带启一下」。本片只做「启动时顺带探一下」,**默认关**(主人 早前说过「开机自启先不急 后面做」),未开开关时连探活都不发。 - 新包 internal/runtime/voice/ttslaunch:探 http://127.0.0.1:9880(毫秒级超时), 已在跑什么都不做;没起则异步起脚本(cmd.exe /c 传参,不拼命令行字符串) - 防重复:in-flight 单飞 + 一次尝试后的冷却(失败也算一次,重启风暴不会变成重试风暴) - 失败只 WARN:探活超时/脚本不存在/起进程失败都只记带 source 的日志, 组合根那条路径**没有 error 通道** - 配置走面板文件 $DATA_DIR/config/voice-tts-launch.json(零新 .env 键) - 接线在 cmd/cyrene 的网关 Listen 之后,整段在后台 goroutine 里,不占启动时长 (用例以「挂载耗时 < 150ms 而探活超时 800ms」证明) - 不新开端点、不加帧族;不碰 gateway 包
This commit is contained in:
1 parent
af61b8ccfe
commit
6906d274fc
13 files changed
+1354
No files matched your search
@@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
// 本文件把**后端启动时顺带照顾 emotionTTS**(切片 M47)接进组合根,并守住三条
|
||||
// 红线(塔 2026-10 已定,主人原话见 internal/runtime/voice/ttslaunch 包注释):
|
||||
//
|
||||
// 1. **默认关**:面板文件 `$DATA_DIR/config/voice-tts-launch.json` 没写
|
||||
// `{"enabled": true}` 时,这里**连探活都不发**、也不构造任何后台任务 ——
|
||||
// 主人早前说过「开机自启先不急 后面做」。
|
||||
// 2. **不占启动时长**:`Trigger` 把探活 + 起脚本整段丢进后台 goroutine 后立即
|
||||
// 返回,调用点在网关 Listen 之后(见 main.run);探活超时/脚本拉起耗时都
|
||||
// 落不到后端的启动路径上。
|
||||
// 3. **失败只 WARN**:脚本不存在、起进程失败、探活超时都只是带 source 的日志,
|
||||
// 本函数**没有 error 返回**,绝不阻塞或改坏后端启动。
|
||||
//
|
||||
// 零新 `.env` 键(`.env` 已冻结):开关与脚本路径都走面板文件家族
|
||||
// (`runtimeconfig`,与 `voice-tts.json` 同一套读取口径)。
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/config/runtimeconfig"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/logger"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/voice/ttslaunch"
|
||||
)
|
||||
|
||||
// voiceTTSLaunchSource 是本切片日志的来源标签(AGENTS §10:日志必须标来源)。
|
||||
const voiceTTSLaunchSource = "cmd/cyrene/voice-tts-launch"
|
||||
|
||||
// startVoiceTTSLaunch 读面板配置,按需投递**一次**「探活 → 必要时拉起」。
|
||||
//
|
||||
// 返回 nil 表示这一级没启用(没配过 / 显式 false / 文件坏掉),调用方据此什么都
|
||||
// 不做;返回非 nil 仅为了让调用方在关闭期或验收里能 `Wait()`(生产路径忽略它)。
|
||||
func (a *app) startVoiceTTSLaunch(ctx context.Context) *ttslaunch.Launcher {
|
||||
log := logger.WithSource(a.log, voiceTTSLaunchSource)
|
||||
path := runtimeconfig.DefaultVoiceTTSLaunchPath(resolveDataDir(a.cfg))
|
||||
tuning, err := runtimeconfig.LoadVoiceTTSLaunchTuning(path)
|
||||
if err != nil {
|
||||
// 坏文件 ⇒ **不启用**:一份读不懂的配置绝不能变成"随手拉起一个进程"。
|
||||
log.Warn("voice tts launch config unreadable, emotionTTS auto-launch stays off",
|
||||
"path", runtimeconfig.VoiceTTSLaunchConfigRelPath, "err", err)
|
||||
return nil
|
||||
}
|
||||
if !tuning.EnabledOn() {
|
||||
log.Debug("emotionTTS auto-launch not enabled in panel config",
|
||||
"path", runtimeconfig.VoiceTTSLaunchConfigRelPath, "hint", `set {"enabled": true}`)
|
||||
return nil
|
||||
}
|
||||
launcher := ttslaunch.New(voiceTTSLaunchConfig(tuning), ttslaunch.WithLogger(log))
|
||||
cfg := launcher.Config()
|
||||
log.Info("emotionTTS auto-launch enabled",
|
||||
"probe_url", cfg.ProbeURL,
|
||||
"script", cfg.ScriptPath,
|
||||
"probe_timeout_ms", cfg.ProbeTimeout.Milliseconds(),
|
||||
"cooldown_sec", int(cfg.Cooldown/time.Second),
|
||||
"config", runtimeconfig.VoiceTTSLaunchConfigRelPath)
|
||||
// 立即返回:探活与起脚本都在后台跑(见 ttslaunch.Launcher.Trigger)。
|
||||
launcher.Trigger(ctx)
|
||||
return launcher
|
||||
}
|
||||
|
||||
// voiceTTSLaunchConfig 把面板形状映射成 launcher 的配置。
|
||||
//
|
||||
// 它**不**在这里裁决缺省值(脚本路径/探活地址/超时/冷却的默认值全在
|
||||
// `ttslaunch.Config.Normalized` 一处):零值原样传下去,由那一处补齐。
|
||||
func voiceTTSLaunchConfig(t runtimeconfig.VoiceTTSLaunchTuning) ttslaunch.Config {
|
||||
return ttslaunch.Config{
|
||||
Enabled: t.EnabledOn(),
|
||||
ScriptPath: t.ScriptPath,
|
||||
ProbeURL: t.ProbeURL,
|
||||
ProbeTimeout: time.Duration(t.ProbeTimeoutMs) * time.Millisecond,
|
||||
Cooldown: time.Duration(t.CooldownSec) * time.Second,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user