@goodandready/dsh-voice 0.9.27 → 0.9.28
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/README.md +1 -1
- package/README.ru.md +1 -1
- package/README.zh.md +1 -1
- package/lib/client.js +4 -0
- package/lib/host-routes.js +2 -0
- package/lib/index.js +43 -3
- package/lib/sensevoice-installer.js +25 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -219,7 +219,7 @@ Registers `transcribe_audio(file_path, language?)` in `ctx.tools`, allowing agen
|
|
|
219
219
|
### Internal HTTP Endpoints
|
|
220
220
|
* `POST /dsh-voice/transcribe` — `{ dataBase64, mimeType, mode }` → `{ ok, text, provider, tookMs }`
|
|
221
221
|
* `POST /dsh-voice/polish` — `{ text }` → `{ ok, text }`
|
|
222
|
-
* `GET /dsh-voice/status` — Returns daemon status, active chains, SenseVoice
|
|
222
|
+
* `GET /dsh-voice/status` — Returns daemon status, active chains, SenseVoice and `effectiveSensevoiceProvider`.
|
|
223
223
|
* `GET /dsh-voice/config` — Returns live plugin configuration snapshot.
|
|
224
224
|
* `PUT /dsh-voice/config` — Updates and persists plugin configuration across network.
|
|
225
225
|
* `GET/POST /dsh-voice/sensevoice-installer` — SenseVoice 1-click model installation status and trigger (protected by `isTrustedCaller`, model paths redacted).
|
package/README.ru.md
CHANGED
|
@@ -219,7 +219,7 @@ dsh plugin --profile web add @goodandready/dsh-voice
|
|
|
219
219
|
### Внутренние HTTP эндпоинты
|
|
220
220
|
* `POST /dsh-voice/transcribe` — `{ dataBase64, mimeType, mode }` → `{ ok, text, provider, tookMs }`
|
|
221
221
|
* `POST /dsh-voice/polish` — `{ text }` → `{ ok, text }`
|
|
222
|
-
* `GET /dsh-voice/status` — состояние демонов, цепочки, конфигурация SenseVoice и
|
|
222
|
+
* `GET /dsh-voice/status` — состояние демонов, цепочки, конфигурация SenseVoice, реалтайма и effectiveSensevoiceProvider.
|
|
223
223
|
* `GET/PUT /dsh-voice/config` — получение и сохранение конфигурации плагина.
|
|
224
224
|
* `GET/POST /dsh-voice/sensevoice-installer` — статус и установка модели SenseVoice в 1 клик.
|
|
225
225
|
* `POST /api/dsh-voice/update` — запуск обновления плагина до актуальной версии из npm (с проверкой прав и блокировки профиля).
|
package/README.zh.md
CHANGED
|
@@ -138,7 +138,7 @@ v0.8.18 质量审查后的修复批次(Gitea #79–#87,PR #88):
|
|
|
138
138
|
|
|
139
139
|
* `POST /dsh-voice/transcribe` — 音频转写:`{ dataBase64, mimeType, mode }` → `{ ok, text, provider, tookMs }`
|
|
140
140
|
* `POST /dsh-voice/polish` — 文本模型润色:`{ text }` → `{ ok, text }`
|
|
141
|
-
* `GET /dsh-voice/status` —
|
|
141
|
+
* `GET /dsh-voice/status` — 查询后端状态、服务商健康度、SenseVoice 及 effectiveSensevoiceProvider
|
|
142
142
|
* `GET /dsh-voice/config` — 获取插件运行时配置快照
|
|
143
143
|
* `PUT /dsh-voice/config` — 跨网络安全持久化更新插件配置
|
|
144
144
|
* `GET/POST /dsh-voice/sensevoice-installer` — SenseVoice 一键模型安装状态与触发接口(受 `isTrustedCaller` 保护,绝对路径脱敏)
|
package/lib/client.js
CHANGED
package/lib/host-routes.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// lib/host-routes.js — Host HTTP routes for dsh-voice status, polish, and transcribe
|
|
2
2
|
import { writeJson, readBody, isTrustedCaller } from './http-util.js'
|
|
3
|
+
import { resolveSensevoiceProvider } from './sensevoice-installer.js'
|
|
3
4
|
|
|
4
5
|
export function registerHostRoutes(ctx, {
|
|
5
6
|
live,
|
|
@@ -63,6 +64,7 @@ export function registerHostRoutes(ctx, {
|
|
|
63
64
|
sensevoiceUrl: cfg.sensevoiceUrl,
|
|
64
65
|
sensevoiceAutostart: !!cfg.sensevoiceAutostart,
|
|
65
66
|
sensevoiceProvider: cfg.sensevoiceProvider || 'cpu',
|
|
67
|
+
effectiveSensevoiceProvider: resolveSensevoiceProvider(cfg),
|
|
66
68
|
sensevoiceThreads: cfg.sensevoiceThreads || 4,
|
|
67
69
|
webgpuModel: cfg.webgpuModel || 'onnx-community/whisper-tiny',
|
|
68
70
|
deepgramBaseUrl: cfg.deepgramBaseUrl || 'https://api.deepgram.com',
|
package/lib/index.js
CHANGED
|
@@ -47,6 +47,23 @@ export const Config = typeof z.number().volatile === 'function'
|
|
|
47
47
|
? makeVolatile(BaseConfig)
|
|
48
48
|
: BaseConfig
|
|
49
49
|
|
|
50
|
+
export function isVolatileRef(value) {
|
|
51
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value) && typeof value.get === 'function'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function plainConfig(value) {
|
|
55
|
+
while (typeof value === 'function') {
|
|
56
|
+
value = value()
|
|
57
|
+
}
|
|
58
|
+
if (isVolatileRef(value)) return plainConfig(value.get())
|
|
59
|
+
if (value === null || typeof value !== 'object') return value
|
|
60
|
+
if (value instanceof Date || value instanceof RegExp || (typeof Buffer !== 'undefined' && Buffer.isBuffer(value))) {
|
|
61
|
+
return value
|
|
62
|
+
}
|
|
63
|
+
if (Array.isArray(value)) return value.map(plainConfig)
|
|
64
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, plainConfig(v)]))
|
|
65
|
+
}
|
|
66
|
+
|
|
50
67
|
export function apply(ctx, baseConfig) {
|
|
51
68
|
let settingsService = null
|
|
52
69
|
let scope = null
|
|
@@ -55,7 +72,25 @@ export function apply(ctx, baseConfig) {
|
|
|
55
72
|
const readEntryConfig = createConfigReader(baseConfig, ctx)
|
|
56
73
|
getConfig = readEntryConfig
|
|
57
74
|
|
|
58
|
-
const
|
|
75
|
+
const describeRow = () => {
|
|
76
|
+
if (typeof settingsService?.describe !== 'function') return null
|
|
77
|
+
try {
|
|
78
|
+
return settingsService.describe().find((r) => r && r.ns === NS) || null
|
|
79
|
+
} catch {
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const live = () => {
|
|
85
|
+
try {
|
|
86
|
+
const raw = getConfig() ?? {}
|
|
87
|
+
const unwrapped = plainConfig(raw)
|
|
88
|
+
const validated = BaseConfig(unwrapped)
|
|
89
|
+
return plainConfig(validated) ?? plainConfig(readEntryConfig())
|
|
90
|
+
} catch {
|
|
91
|
+
return plainConfig(readEntryConfig())
|
|
92
|
+
}
|
|
93
|
+
}
|
|
59
94
|
|
|
60
95
|
const resolveKey = async (ref) => {
|
|
61
96
|
if (!ref || !ref.type) return ''
|
|
@@ -81,14 +116,19 @@ export function apply(ctx, baseConfig) {
|
|
|
81
116
|
try {
|
|
82
117
|
scope = sctx.settings.register(NS, Config, { base: baseConfig })
|
|
83
118
|
if (scope && typeof scope.get === 'function') {
|
|
84
|
-
getConfig = () => scope.get() ?? readEntryConfig()
|
|
119
|
+
getConfig = () => scope.get() ?? describeRow()?.config ?? readEntryConfig()
|
|
120
|
+
} else {
|
|
121
|
+
getConfig = () => describeRow()?.config ?? readEntryConfig()
|
|
85
122
|
}
|
|
86
123
|
if (scope && typeof scope.watch === 'function') {
|
|
87
124
|
scope.watch(() => {
|
|
88
125
|
triggerAutostart()
|
|
89
126
|
})
|
|
90
127
|
}
|
|
91
|
-
} catch {
|
|
128
|
+
} catch {
|
|
129
|
+
// Modern DSH 0.2 handles settings via SettingsForms and describe/update/replace
|
|
130
|
+
getConfig = () => describeRow()?.config ?? readEntryConfig()
|
|
131
|
+
}
|
|
92
132
|
sctx.effect(() => () => { getConfig = readEntryConfig; scope = null; settingsService = null })
|
|
93
133
|
triggerAutostart()
|
|
94
134
|
})
|
|
@@ -231,6 +231,31 @@ export function registerSensevoiceInstaller(ctx, { liveConfig, isAlive, updateCo
|
|
|
231
231
|
})
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
export function resolveSensevoiceProvider(cfg) {
|
|
235
|
+
let modelPath = String(cfg?.sensevoiceModel || '')
|
|
236
|
+
const rawProv = String(cfg?.sensevoiceProvider || '').trim().toLowerCase()
|
|
237
|
+
let normProv = rawProv === 'rknpu' ? 'rknn' : rawProv
|
|
238
|
+
|
|
239
|
+
if (normProv === 'rknn') {
|
|
240
|
+
const ext = path.extname(modelPath).toLowerCase()
|
|
241
|
+
if (ext === '.onnx') {
|
|
242
|
+
const modelDir = modelPath ? path.dirname(modelPath) : getDefaultModelDir()
|
|
243
|
+
const rknnCandidate = path.join(modelDir, path.basename(modelPath, ext) + '.rknn')
|
|
244
|
+
const defaultRknn = path.join(modelDir, 'model.rknn')
|
|
245
|
+
if (existsSync(rknnCandidate) || existsSync(defaultRknn)) {
|
|
246
|
+
return 'rknn'
|
|
247
|
+
}
|
|
248
|
+
// Guard: ordinary ONNX model cannot be loaded by sherpa-onnx RKNN runtime (#221)
|
|
249
|
+
return 'cpu'
|
|
250
|
+
}
|
|
251
|
+
if (ext === '.rknn') {
|
|
252
|
+
return 'rknn'
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return normProv || 'cpu'
|
|
257
|
+
}
|
|
258
|
+
|
|
234
259
|
export function buildSensevoiceArgs(cfg, port) {
|
|
235
260
|
let modelPath = String(cfg?.sensevoiceModel || '')
|
|
236
261
|
const modelDir = modelPath ? path.dirname(modelPath) : getDefaultModelDir()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-voice",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.28",
|
|
4
4
|
"description": "Voice input for DeepSeek Harness: dictation chunked by pauses and voice messages, each with its own provider fallback chain (Deepgram, Groq, HuggingFace, local whisper.cpp, plus any OpenAI-compatible API of your own).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|