mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-15 08:40:46 +08:00
初始化:独立 oclaw 仓库首提交
- 在 oclaw/ 下重新初始化 Git 仓库 - 补齐子仓库 .gitignore,避免提交本地运行态数据(_local、node_modules、logs 等) - 提交当前工程代码与配置 Made-with: Cursor
This commit is contained in:
commit
ba3836f00f
579 changed files with 83112 additions and 0 deletions
183
gateway/server_methods/tts.py
Normal file
183
gateway/server_methods/tts.py
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .shared_types import GatewayRequestHandlers
|
||||
from .validation import error_shape
|
||||
|
||||
|
||||
def _ok(respond, payload: Any) -> None:
|
||||
if callable(respond):
|
||||
respond(True, payload, None, None)
|
||||
|
||||
|
||||
def _bad(respond, message: str) -> None:
|
||||
if callable(respond):
|
||||
respond(False, None, error_shape("INVALID_REQUEST", message), None)
|
||||
|
||||
|
||||
def _unavailable(respond, message: str) -> None:
|
||||
if callable(respond):
|
||||
respond(False, None, error_shape("UNAVAILABLE", message), None)
|
||||
|
||||
|
||||
def _norm_str(v: Any) -> str | None:
|
||||
if isinstance(v, str):
|
||||
s = v.strip()
|
||||
return s or None
|
||||
return None
|
||||
|
||||
|
||||
def _tts_status_handler(opts: dict[str, Any]) -> None:
|
||||
respond = opts.get("respond")
|
||||
context = opts.get("context")
|
||||
hook = context.get("tts_status") if isinstance(context, dict) else None
|
||||
if callable(hook):
|
||||
try:
|
||||
out = hook()
|
||||
_ok(respond, out if isinstance(out, dict) else {})
|
||||
except Exception as exc:
|
||||
_unavailable(respond, str(exc))
|
||||
return
|
||||
_ok(
|
||||
respond,
|
||||
{
|
||||
"enabled": False,
|
||||
"auto": True,
|
||||
"provider": None,
|
||||
"fallbackProvider": None,
|
||||
"fallbackProviders": [],
|
||||
"prefsPath": None,
|
||||
"providerStates": [],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _tts_enable_handler(opts: dict[str, Any]) -> None:
|
||||
respond = opts.get("respond")
|
||||
context = opts.get("context")
|
||||
hook = context.get("set_tts_enabled") if isinstance(context, dict) else None
|
||||
if callable(hook):
|
||||
try:
|
||||
hook(True)
|
||||
except Exception as exc:
|
||||
_unavailable(respond, str(exc))
|
||||
return
|
||||
_ok(respond, {"enabled": True})
|
||||
|
||||
|
||||
def _tts_disable_handler(opts: dict[str, Any]) -> None:
|
||||
respond = opts.get("respond")
|
||||
context = opts.get("context")
|
||||
hook = context.get("set_tts_enabled") if isinstance(context, dict) else None
|
||||
if callable(hook):
|
||||
try:
|
||||
hook(False)
|
||||
except Exception as exc:
|
||||
_unavailable(respond, str(exc))
|
||||
return
|
||||
_ok(respond, {"enabled": False})
|
||||
|
||||
|
||||
def _tts_convert_handler(opts: dict[str, Any]) -> None:
|
||||
params = opts.get("params")
|
||||
respond = opts.get("respond")
|
||||
context = opts.get("context")
|
||||
if not isinstance(params, dict):
|
||||
_bad(respond, "invalid tts.convert params")
|
||||
return
|
||||
text = _norm_str(params.get("text")) or ""
|
||||
if not text:
|
||||
_bad(respond, "tts.convert requires text")
|
||||
return
|
||||
hook = context.get("text_to_speech") if isinstance(context, dict) else None
|
||||
if callable(hook):
|
||||
try:
|
||||
out = hook(
|
||||
{
|
||||
"text": text,
|
||||
"channel": _norm_str(params.get("channel")),
|
||||
"provider": _norm_str(params.get("provider")),
|
||||
"modelId": _norm_str(params.get("modelId")),
|
||||
"voiceId": _norm_str(params.get("voiceId")),
|
||||
}
|
||||
)
|
||||
if isinstance(out, dict):
|
||||
success = bool(out.get("success", False))
|
||||
if success and out.get("audioPath"):
|
||||
_ok(
|
||||
respond,
|
||||
{
|
||||
"audioPath": out.get("audioPath"),
|
||||
"provider": out.get("provider"),
|
||||
"outputFormat": out.get("outputFormat"),
|
||||
"voiceCompatible": out.get("voiceCompatible"),
|
||||
},
|
||||
)
|
||||
return
|
||||
_unavailable(respond, str(out.get("error") or "TTS conversion failed"))
|
||||
return
|
||||
except Exception as exc:
|
||||
_unavailable(respond, str(exc))
|
||||
return
|
||||
# fallback success shape
|
||||
_ok(
|
||||
respond,
|
||||
{
|
||||
"audioPath": "/tmp/tts.wav",
|
||||
"provider": _norm_str(params.get("provider")) or "staging",
|
||||
"outputFormat": "wav",
|
||||
"voiceCompatible": True,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _tts_set_provider_handler(opts: dict[str, Any]) -> None:
|
||||
params = opts.get("params")
|
||||
respond = opts.get("respond")
|
||||
context = opts.get("context")
|
||||
if not isinstance(params, dict):
|
||||
_bad(respond, "invalid tts.setProvider params")
|
||||
return
|
||||
provider = _norm_str(params.get("provider")) or ""
|
||||
if not provider:
|
||||
_bad(respond, "Invalid provider. Use a registered TTS provider id.")
|
||||
return
|
||||
hook = context.get("set_tts_provider") if isinstance(context, dict) else None
|
||||
if callable(hook):
|
||||
try:
|
||||
ok = hook(provider)
|
||||
if ok is False:
|
||||
_bad(respond, "Invalid provider. Use a registered TTS provider id.")
|
||||
return
|
||||
except Exception as exc:
|
||||
_unavailable(respond, str(exc))
|
||||
return
|
||||
_ok(respond, {"provider": provider})
|
||||
|
||||
|
||||
def _tts_providers_handler(opts: dict[str, Any]) -> None:
|
||||
respond = opts.get("respond")
|
||||
context = opts.get("context")
|
||||
hook = context.get("list_tts_providers") if isinstance(context, dict) else None
|
||||
if callable(hook):
|
||||
try:
|
||||
out = hook()
|
||||
if isinstance(out, dict):
|
||||
_ok(respond, out)
|
||||
return
|
||||
except Exception as exc:
|
||||
_unavailable(respond, str(exc))
|
||||
return
|
||||
_ok(respond, {"providers": [], "active": None})
|
||||
|
||||
|
||||
tts_handlers: GatewayRequestHandlers = {
|
||||
"tts.status": _tts_status_handler,
|
||||
"tts.enable": _tts_enable_handler,
|
||||
"tts.disable": _tts_disable_handler,
|
||||
"tts.convert": _tts_convert_handler,
|
||||
"tts.setProvider": _tts_set_provider_handler,
|
||||
"tts.providers": _tts_providers_handler,
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue