@downcity/plugins 1.0.56 → 1.0.57
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/bin/BuiltinPlugins.d.ts.map +1 -1
- package/bin/BuiltinPlugins.js +0 -4
- package/bin/BuiltinPlugins.js.map +1 -1
- package/bin/asr/Plugin.d.ts +67 -7
- package/bin/asr/Plugin.d.ts.map +1 -1
- package/bin/asr/Plugin.js +229 -461
- package/bin/asr/Plugin.js.map +1 -1
- package/bin/asr/types/AsrPlugin.d.ts +114 -0
- package/bin/asr/types/AsrPlugin.d.ts.map +1 -0
- package/bin/asr/types/AsrPlugin.js +10 -0
- package/bin/asr/types/AsrPlugin.js.map +1 -0
- package/bin/image/ImagePlugin.d.ts +1 -1
- package/bin/image/ImagePlugin.d.ts.map +1 -1
- package/bin/image/ImagePlugin.js +23 -5
- package/bin/image/ImagePlugin.js.map +1 -1
- package/bin/index.d.ts +2 -0
- package/bin/index.d.ts.map +1 -1
- package/bin/tts/Plugin.d.ts +53 -6
- package/bin/tts/Plugin.d.ts.map +1 -1
- package/bin/tts/Plugin.js +197 -474
- package/bin/tts/Plugin.js.map +1 -1
- package/bin/tts/types/TtsPlugin.d.ts +63 -102
- package/bin/tts/types/TtsPlugin.d.ts.map +1 -1
- package/bin/tts/types/TtsPlugin.js +4 -3
- package/bin/tts/types/TtsPlugin.js.map +1 -1
- package/bin/web/PROMPT.d.ts +1 -1
- package/bin/web/PROMPT.d.ts.map +1 -1
- package/bin/web/PROMPT.js +1 -1
- package/bin/web/PROMPT.js.map +1 -1
- package/bin/web/Plugin.d.ts +66 -5
- package/bin/web/Plugin.d.ts.map +1 -1
- package/bin/web/Plugin.js +126 -450
- package/bin/web/Plugin.js.map +1 -1
- package/bin/web/WebPromptAssets.d.ts +1 -9
- package/bin/web/WebPromptAssets.d.ts.map +1 -1
- package/bin/web/WebPromptAssets.js +1 -11
- package/bin/web/WebPromptAssets.js.map +1 -1
- package/bin/web/runtime/Install.d.ts +19 -0
- package/bin/web/runtime/Install.d.ts.map +1 -0
- package/bin/web/runtime/Install.js +178 -0
- package/bin/web/runtime/Install.js.map +1 -0
- package/bin/web/types/WebPlugin.d.ts +37 -109
- package/bin/web/types/WebPlugin.d.ts.map +1 -1
- package/bin/web/types/WebPlugin.js +5 -7
- package/bin/web/types/WebPlugin.js.map +1 -1
- package/package.json +2 -2
- package/src/BuiltinPlugins.ts +0 -4
- package/src/asr/Plugin.ts +264 -483
- package/src/asr/types/AsrPlugin.ts +118 -0
- package/src/image/ImagePlugin.ts +23 -5
- package/src/index.ts +12 -0
- package/src/tts/Plugin.ts +225 -492
- package/src/tts/types/TtsPlugin.ts +67 -102
- package/src/web/PROMPT.ts +1 -1
- package/src/web/PROMPT.ts.txt +32 -6
- package/src/web/Plugin.ts +119 -453
- package/src/web/WebPromptAssets.ts +1 -13
- package/src/web/runtime/Install.ts +241 -0
- package/src/web/types/WebPlugin.ts +37 -113
- package/bin/asr/Config.d.ts +0 -43
- package/bin/asr/Config.d.ts.map +0 -1
- package/bin/asr/Config.js +0 -107
- package/bin/asr/Config.js.map +0 -1
- package/bin/asr/Dependency.d.ts +0 -77
- package/bin/asr/Dependency.d.ts.map +0 -1
- package/bin/asr/Dependency.js +0 -238
- package/bin/asr/Dependency.js.map +0 -1
- package/bin/asr/InboundAugment.d.ts +0 -17
- package/bin/asr/InboundAugment.d.ts.map +0 -1
- package/bin/asr/InboundAugment.js +0 -47
- package/bin/asr/InboundAugment.js.map +0 -1
- package/bin/asr/ModelCatalog.d.ts +0 -29
- package/bin/asr/ModelCatalog.d.ts.map +0 -1
- package/bin/asr/ModelCatalog.js +0 -25
- package/bin/asr/ModelCatalog.js.map +0 -1
- package/bin/tts/Dependency.d.ts +0 -90
- package/bin/tts/Dependency.d.ts.map +0 -1
- package/bin/tts/Dependency.js +0 -344
- package/bin/tts/Dependency.js.map +0 -1
- package/bin/tts/PluginSupport.d.ts +0 -25
- package/bin/tts/PluginSupport.d.ts.map +0 -1
- package/bin/tts/PluginSupport.js +0 -72
- package/bin/tts/PluginSupport.js.map +0 -1
- package/bin/tts/runtime/Catalog.d.ts +0 -21
- package/bin/tts/runtime/Catalog.d.ts.map +0 -1
- package/bin/tts/runtime/Catalog.js +0 -90
- package/bin/tts/runtime/Catalog.js.map +0 -1
- package/bin/tts/runtime/DependencyInstaller.d.ts +0 -143
- package/bin/tts/runtime/DependencyInstaller.d.ts.map +0 -1
- package/bin/tts/runtime/DependencyInstaller.js +0 -261
- package/bin/tts/runtime/DependencyInstaller.js.map +0 -1
- package/bin/tts/runtime/Installer.d.ts +0 -89
- package/bin/tts/runtime/Installer.d.ts.map +0 -1
- package/bin/tts/runtime/Installer.js +0 -188
- package/bin/tts/runtime/Installer.js.map +0 -1
- package/bin/tts/runtime/Paths.d.ts +0 -20
- package/bin/tts/runtime/Paths.d.ts.map +0 -1
- package/bin/tts/runtime/Paths.js +0 -32
- package/bin/tts/runtime/Paths.js.map +0 -1
- package/bin/tts/runtime/Synthesizer.d.ts +0 -44
- package/bin/tts/runtime/Synthesizer.d.ts.map +0 -1
- package/bin/tts/runtime/Synthesizer.js +0 -363
- package/bin/tts/runtime/Synthesizer.js.map +0 -1
- package/bin/tts/types/Tts.d.ts +0 -91
- package/bin/tts/types/Tts.d.ts.map +0 -1
- package/bin/tts/types/Tts.js +0 -9
- package/bin/tts/types/Tts.js.map +0 -1
- package/bin/voice/Config.d.ts +0 -43
- package/bin/voice/Config.d.ts.map +0 -1
- package/bin/voice/Config.js +0 -104
- package/bin/voice/Config.js.map +0 -1
- package/bin/voice/Dependency.d.ts +0 -77
- package/bin/voice/Dependency.d.ts.map +0 -1
- package/bin/voice/Dependency.js +0 -237
- package/bin/voice/Dependency.js.map +0 -1
- package/bin/voice/InboundAugment.d.ts +0 -17
- package/bin/voice/InboundAugment.d.ts.map +0 -1
- package/bin/voice/InboundAugment.js +0 -47
- package/bin/voice/InboundAugment.js.map +0 -1
- package/bin/voice/ModelCatalog.d.ts +0 -29
- package/bin/voice/ModelCatalog.d.ts.map +0 -1
- package/bin/voice/ModelCatalog.js +0 -25
- package/bin/voice/ModelCatalog.js.map +0 -1
- package/bin/voice/runtime/Catalog.d.ts +0 -18
- package/bin/voice/runtime/Catalog.d.ts.map +0 -1
- package/bin/voice/runtime/Catalog.js +0 -61
- package/bin/voice/runtime/Catalog.js.map +0 -1
- package/bin/voice/runtime/DependencyInstaller.d.ts +0 -145
- package/bin/voice/runtime/DependencyInstaller.d.ts.map +0 -1
- package/bin/voice/runtime/DependencyInstaller.js +0 -309
- package/bin/voice/runtime/DependencyInstaller.js.map +0 -1
- package/bin/voice/runtime/Installer.d.ts +0 -94
- package/bin/voice/runtime/Installer.d.ts.map +0 -1
- package/bin/voice/runtime/Installer.js +0 -200
- package/bin/voice/runtime/Installer.js.map +0 -1
- package/bin/voice/runtime/Paths.d.ts +0 -8
- package/bin/voice/runtime/Paths.d.ts.map +0 -1
- package/bin/voice/runtime/Paths.js +0 -26
- package/bin/voice/runtime/Paths.js.map +0 -1
- package/bin/voice/runtime/Transcriber.d.ts +0 -57
- package/bin/voice/runtime/Transcriber.d.ts.map +0 -1
- package/bin/voice/runtime/Transcriber.js +0 -329
- package/bin/voice/runtime/Transcriber.js.map +0 -1
- package/bin/voice/types/Voice.d.ts +0 -58
- package/bin/voice/types/Voice.d.ts.map +0 -1
- package/bin/voice/types/Voice.js +0 -9
- package/bin/voice/types/Voice.js.map +0 -1
- package/bin/voice/types/VoicePlugin.d.ts +0 -190
- package/bin/voice/types/VoicePlugin.d.ts.map +0 -1
- package/bin/voice/types/VoicePlugin.js +0 -9
- package/bin/voice/types/VoicePlugin.js.map +0 -1
- package/bin/web/Dependency.d.ts +0 -10
- package/bin/web/Dependency.d.ts.map +0 -1
- package/bin/web/Dependency.js +0 -10
- package/bin/web/Dependency.js.map +0 -1
- package/bin/web/PROMPT.agent-browser.d.ts +0 -7
- package/bin/web/PROMPT.agent-browser.d.ts.map +0 -1
- package/bin/web/PROMPT.agent-browser.js +0 -8
- package/bin/web/PROMPT.agent-browser.js.map +0 -1
- package/bin/web/PROMPT.web-access.d.ts +0 -7
- package/bin/web/PROMPT.web-access.d.ts.map +0 -1
- package/bin/web/PROMPT.web-access.js +0 -8
- package/bin/web/PROMPT.web-access.js.map +0 -1
- package/bin/web/runtime/Config.d.ts +0 -21
- package/bin/web/runtime/Config.d.ts.map +0 -1
- package/bin/web/runtime/Config.js +0 -79
- package/bin/web/runtime/Config.js.map +0 -1
- package/bin/web/runtime/Source.d.ts +0 -29
- package/bin/web/runtime/Source.d.ts.map +0 -1
- package/bin/web/runtime/Source.js +0 -209
- package/bin/web/runtime/Source.js.map +0 -1
- package/src/asr/Config.ts +0 -138
- package/src/asr/Dependency.ts +0 -336
- package/src/asr/InboundAugment.ts +0 -59
- package/src/asr/ModelCatalog.ts +0 -43
- package/src/tts/Dependency.ts +0 -473
- package/src/tts/PluginSupport.ts +0 -85
- package/src/tts/runtime/Catalog.ts +0 -97
- package/src/tts/runtime/DependencyInstaller.ts +0 -436
- package/src/tts/runtime/Installer.ts +0 -297
- package/src/tts/runtime/Paths.ts +0 -39
- package/src/tts/runtime/Synthesizer.ts +0 -480
- package/src/tts/types/Tts.ts +0 -99
- package/src/voice/Config.ts +0 -135
- package/src/voice/Dependency.ts +0 -329
- package/src/voice/InboundAugment.ts +0 -59
- package/src/voice/ModelCatalog.ts +0 -43
- package/src/voice/runtime/Catalog.ts +0 -68
- package/src/voice/runtime/DependencyInstaller.ts +0 -505
- package/src/voice/runtime/Installer.ts +0 -324
- package/src/voice/runtime/Paths.ts +0 -26
- package/src/voice/runtime/Transcriber.ts +0 -467
- package/src/voice/types/Voice.ts +0 -68
- package/src/voice/types/VoicePlugin.ts +0 -194
- package/src/web/Dependency.ts +0 -17
- package/src/web/PROMPT.agent-browser.ts +0 -9
- package/src/web/PROMPT.agent-browser.ts.txt +0 -17
- package/src/web/PROMPT.web-access.ts +0 -9
- package/src/web/PROMPT.web-access.ts.txt +0 -13
- package/src/web/runtime/Config.ts +0 -105
- package/src/web/runtime/Source.ts +0 -257
package/bin/asr/Plugin.js
CHANGED
|
@@ -1,481 +1,249 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* AsrPlugin:语音转写插件。
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
|
-
* - ASR
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
5
|
+
* - ASR 能力通过 constructor 注入,推荐传入 `city.ai.asr`。
|
|
6
|
+
* - plugin 不负责本地模型、Python 依赖、provider 或项目配置。
|
|
7
|
+
* - `auto: true` 时会在 chat 入站阶段自动转写 voice/audio 附件,并把结果写入正文。
|
|
8
8
|
*/
|
|
9
|
+
import path from "node:path";
|
|
9
10
|
import { BasePlugin } from "@downcity/agent/internal/plugin/core/BasePlugin.js";
|
|
10
11
|
import { CHAT_PLUGIN_POINTS } from "../chat/runtime/PluginPoints.js";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
function
|
|
12
|
+
const DEFAULT_ASR_PLUGIN_NAME = "asr";
|
|
13
|
+
const DEFAULT_ASR_PLUGIN_TITLE = "ASR";
|
|
14
|
+
const DEFAULT_ASR_PLUGIN_DESCRIPTION = "Transcribe voice and audio attachments through an injected ASR function.";
|
|
15
|
+
/**
|
|
16
|
+
* 判断值是否为普通对象。
|
|
17
|
+
*/
|
|
18
|
+
function to_record(value) {
|
|
19
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
20
|
+
return null;
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* XML 文本转义。
|
|
25
|
+
*/
|
|
26
|
+
function escape_xml_text(value) {
|
|
27
|
+
return String(value || "")
|
|
28
|
+
.replace(/&/g, "&")
|
|
29
|
+
.replace(/</g, "<")
|
|
30
|
+
.replace(/>/g, ">");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* XML 属性转义。
|
|
34
|
+
*/
|
|
35
|
+
function escape_xml_attr(value) {
|
|
36
|
+
return escape_xml_text(value).replace(/"/g, """);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 归一化 action payload。
|
|
40
|
+
*/
|
|
41
|
+
function normalize_asr_payload(payload) {
|
|
42
|
+
const record = to_record(payload ?? {});
|
|
43
|
+
if (!record) {
|
|
44
|
+
throw new TypeError("AsrPlugin.transcribe payload must be an object");
|
|
45
|
+
}
|
|
46
|
+
const audio_path = typeof record.audio_path === "string"
|
|
47
|
+
? record.audio_path.trim()
|
|
48
|
+
: "";
|
|
49
|
+
const url = typeof record.url === "string" ? record.url.trim() : "";
|
|
50
|
+
const data_url = typeof record.data_url === "string" ? record.data_url.trim() : "";
|
|
51
|
+
if (!audio_path && !url && !data_url) {
|
|
52
|
+
throw new Error("AsrPlugin.transcribe requires audio_path, url, or data_url");
|
|
53
|
+
}
|
|
18
54
|
return {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
55
|
+
...record,
|
|
56
|
+
...(audio_path ? { audio_path } : {}),
|
|
57
|
+
...(url ? { url } : {}),
|
|
58
|
+
...(data_url ? { data_url } : {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 校验 ASR 返回结果。
|
|
63
|
+
*/
|
|
64
|
+
function normalize_asr_result(result) {
|
|
65
|
+
const record = to_record(result);
|
|
66
|
+
if (!record || typeof record.text !== "string") {
|
|
67
|
+
throw new TypeError("AsrPlugin asr function must return { text: string }");
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
...record,
|
|
71
|
+
text: record.text.trim(),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 生成相对展示路径。
|
|
76
|
+
*/
|
|
77
|
+
function to_display_src(root_path, attachment) {
|
|
78
|
+
const raw = typeof attachment.path === "string" && attachment.path.trim()
|
|
79
|
+
? attachment.path.trim()
|
|
80
|
+
: attachment.fileName || attachment.attachmentId || attachment.kind;
|
|
81
|
+
const normalized_root = path.resolve(root_path);
|
|
82
|
+
const normalized_raw = path.isAbsolute(raw) ? path.resolve(raw) : raw;
|
|
83
|
+
if (path.isAbsolute(normalized_raw) && normalized_raw.startsWith(`${normalized_root}${path.sep}`)) {
|
|
84
|
+
return normalized_raw.slice(normalized_root.length + 1);
|
|
85
|
+
}
|
|
86
|
+
return raw;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 把语音转写结果写入 bodyText。
|
|
90
|
+
*/
|
|
91
|
+
function append_voice_text(input, voice_blocks) {
|
|
92
|
+
const current = String(input.bodyText || "").trim();
|
|
93
|
+
const addition = voice_blocks.map((item) => item.trim()).filter(Boolean).join("\n\n");
|
|
94
|
+
if (!addition)
|
|
95
|
+
return input;
|
|
96
|
+
return {
|
|
97
|
+
...input,
|
|
98
|
+
bodyText: [current, addition].filter(Boolean).join("\n\n"),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Agent ASR 插件。
|
|
103
|
+
*/
|
|
104
|
+
export class AsrPlugin extends BasePlugin {
|
|
105
|
+
/**
|
|
106
|
+
* 当前 plugin 稳定名称。
|
|
107
|
+
*/
|
|
108
|
+
name;
|
|
109
|
+
/**
|
|
110
|
+
* 插件标题。
|
|
111
|
+
*/
|
|
112
|
+
title;
|
|
113
|
+
/**
|
|
114
|
+
* 插件说明。
|
|
115
|
+
*/
|
|
116
|
+
description;
|
|
117
|
+
asr;
|
|
118
|
+
auto;
|
|
119
|
+
language;
|
|
120
|
+
constructor(options) {
|
|
121
|
+
super();
|
|
122
|
+
const name = String(options.name || DEFAULT_ASR_PLUGIN_NAME).trim();
|
|
123
|
+
if (!name) {
|
|
124
|
+
throw new Error("AsrPlugin requires a non-empty name");
|
|
125
|
+
}
|
|
126
|
+
if (typeof options.asr !== "function") {
|
|
127
|
+
throw new Error("AsrPlugin requires an asr function");
|
|
128
|
+
}
|
|
129
|
+
this.name = name;
|
|
130
|
+
this.title = String(options.title || DEFAULT_ASR_PLUGIN_TITLE).trim();
|
|
131
|
+
this.description = String(options.description || DEFAULT_ASR_PLUGIN_DESCRIPTION).trim();
|
|
132
|
+
this.asr = options.asr;
|
|
133
|
+
this.auto = options.auto === true;
|
|
134
|
+
this.language =
|
|
135
|
+
typeof options.language === "string" && options.language.trim()
|
|
136
|
+
? options.language.trim()
|
|
137
|
+
: undefined;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* ASR 插件给模型的使用说明。
|
|
141
|
+
*/
|
|
142
|
+
system(_context) {
|
|
143
|
+
return [
|
|
144
|
+
"# ASR Plugin",
|
|
145
|
+
"",
|
|
146
|
+
"Use this plugin when the user asks to transcribe, recognize, or understand voice/audio content.",
|
|
147
|
+
this.auto
|
|
148
|
+
? "Inbound voice/audio chat attachments are automatically transcribed into `<voice src=\"...\">...</voice>` blocks in the user text."
|
|
149
|
+
: "Automatic inbound voice transcription is disabled; call the plugin explicitly when transcription is needed.",
|
|
150
|
+
"",
|
|
151
|
+
"Call through `plugin_call`:",
|
|
152
|
+
"",
|
|
153
|
+
"```ts",
|
|
154
|
+
"plugin_call({",
|
|
155
|
+
` plugin: "${this.name}",`,
|
|
156
|
+
' action: "transcribe",',
|
|
157
|
+
" payload: {",
|
|
158
|
+
' audio_path: "...",',
|
|
159
|
+
" },",
|
|
160
|
+
"});",
|
|
161
|
+
"```",
|
|
162
|
+
"",
|
|
163
|
+
"Payload rules:",
|
|
164
|
+
"- Provide one of `audio_path`, `url`, or `data_url`.",
|
|
165
|
+
"- Optional fields: `language`, `media_type`, `file_name`, `provider_options`.",
|
|
166
|
+
"- Do not invent transcript text. If transcription fails, report the failure clearly.",
|
|
167
|
+
].join("\n");
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 执行一次 ASR 转写。
|
|
171
|
+
*/
|
|
172
|
+
async transcribe(input) {
|
|
173
|
+
const result = await this.asr({
|
|
174
|
+
...(this.language ? { language: this.language } : {}),
|
|
175
|
+
...input,
|
|
176
|
+
});
|
|
177
|
+
return normalize_asr_result(result);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* 自动增强 chat 入站消息。
|
|
181
|
+
*/
|
|
182
|
+
async auto_transcribe_inbound(params) {
|
|
183
|
+
if (!this.auto)
|
|
184
|
+
return params.value;
|
|
185
|
+
const input = params.value;
|
|
186
|
+
const voice_attachments = (Array.isArray(input.attachments) ? input.attachments : []).filter((item) => (item.kind === "voice" || item.kind === "audio") &&
|
|
187
|
+
typeof item.path === "string" &&
|
|
188
|
+
item.path.trim());
|
|
189
|
+
if (voice_attachments.length === 0) {
|
|
190
|
+
return input;
|
|
191
|
+
}
|
|
192
|
+
const voice_blocks = [];
|
|
193
|
+
for (const attachment of voice_attachments) {
|
|
194
|
+
try {
|
|
195
|
+
const result = await this.transcribe({
|
|
196
|
+
audio_path: String(attachment.path || "").trim(),
|
|
197
|
+
...(attachment.contentType ? { media_type: attachment.contentType } : {}),
|
|
198
|
+
...(attachment.fileName ? { file_name: attachment.fileName } : {}),
|
|
199
|
+
});
|
|
200
|
+
if (!result.text)
|
|
201
|
+
continue;
|
|
202
|
+
const src = to_display_src(params.context.rootPath, attachment);
|
|
203
|
+
voice_blocks.push(`<voice src="${escape_xml_attr(src)}">${escape_xml_text(result.text)}</voice>`);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// 关键点(中文):自动转写失败不阻塞主消息链路。
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return append_voice_text(input, voice_blocks);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* pipeline / action 扩展点。
|
|
213
|
+
*/
|
|
214
|
+
hooks = {
|
|
215
|
+
pipeline: {
|
|
216
|
+
[CHAT_PLUGIN_POINTS.augmentInbound]: [
|
|
217
|
+
async ({ context, value }) => {
|
|
218
|
+
return await this.auto_transcribe_inbound({ context, value });
|
|
90
219
|
},
|
|
91
220
|
],
|
|
92
|
-
saveAction: "configure",
|
|
93
|
-
statusAction: "status",
|
|
94
|
-
},
|
|
95
|
-
async availability(context) {
|
|
96
|
-
if (!isPluginEnabled({ plugin, context })) {
|
|
97
|
-
return {
|
|
98
|
-
enabled: false,
|
|
99
|
-
available: false,
|
|
100
|
-
reasons: ["asr plugin disabled in project config"],
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
const dependencyStatus = await checkVoiceTranscriber(context);
|
|
104
|
-
return {
|
|
105
|
-
enabled: true,
|
|
106
|
-
available: dependencyStatus.available,
|
|
107
|
-
reasons: dependencyStatus.reasons,
|
|
108
|
-
};
|
|
109
221
|
},
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
status: {
|
|
121
|
-
allowWhenDisabled: true,
|
|
122
|
-
command: {
|
|
123
|
-
description: "查看 asr plugin 当前状态",
|
|
124
|
-
mapInput() {
|
|
125
|
-
return {};
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
execute: async ({ context }) => {
|
|
129
|
-
const config = readVoicePluginConfig(context);
|
|
130
|
-
const availability = await plugin.availability(context);
|
|
131
|
-
const transcriberConfig = readVoiceTranscriberConfig(context);
|
|
132
|
-
return {
|
|
133
|
-
success: true,
|
|
134
|
-
data: {
|
|
135
|
-
plugin: toJsonObject(config) || {},
|
|
136
|
-
availability: {
|
|
137
|
-
enabled: availability.enabled,
|
|
138
|
-
available: availability.available,
|
|
139
|
-
reasons: availability.reasons,
|
|
140
|
-
},
|
|
141
|
-
transcriber: toJsonObject((transcriberConfig || null)),
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
install: {
|
|
147
|
-
allowWhenDisabled: true,
|
|
148
|
-
command: {
|
|
149
|
-
description: "安装 asr 转写依赖",
|
|
150
|
-
configure(command) {
|
|
151
|
-
command
|
|
152
|
-
.argument("[models...]")
|
|
153
|
-
.option("--active-model <modelId>", "安装完成后设为当前模型")
|
|
154
|
-
.option("--models-dir <path>", "模型目录(可选)")
|
|
155
|
-
.option("--python <bin>", "Python 可执行文件(默认 python3)")
|
|
156
|
-
.option("--no-install-deps", "跳过依赖安装")
|
|
157
|
-
.option("--force", "强制覆盖已存在资源")
|
|
158
|
-
.option("--hf-token <token>", "HuggingFace token(可选)");
|
|
159
|
-
},
|
|
160
|
-
mapInput({ args, opts }) {
|
|
161
|
-
return {
|
|
162
|
-
modelIds: args,
|
|
163
|
-
...(getStringOpt(opts, "activeModel")
|
|
164
|
-
? { activeModel: getStringOpt(opts, "activeModel") }
|
|
165
|
-
: {}),
|
|
166
|
-
...(getStringOpt(opts, "modelsDir")
|
|
167
|
-
? { modelsDir: getStringOpt(opts, "modelsDir") }
|
|
168
|
-
: {}),
|
|
169
|
-
...(getStringOpt(opts, "python")
|
|
170
|
-
? { pythonBin: getStringOpt(opts, "python") }
|
|
171
|
-
: {}),
|
|
172
|
-
installDeps: getBooleanOpt(opts, "installDeps", true),
|
|
173
|
-
force: getBooleanOpt(opts, "force", false),
|
|
174
|
-
...(getStringOpt(opts, "hfToken")
|
|
175
|
-
? { hfToken: getStringOpt(opts, "hfToken") }
|
|
176
|
-
: {}),
|
|
177
|
-
};
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
execute: async ({ context, payload }) => {
|
|
181
|
-
const result = await installVoiceTranscriber({
|
|
182
|
-
context,
|
|
183
|
-
input: payload && typeof payload === "object" && !Array.isArray(payload)
|
|
184
|
-
? payload
|
|
185
|
-
: undefined,
|
|
186
|
-
});
|
|
187
|
-
return {
|
|
188
|
-
success: result.success,
|
|
189
|
-
...(result.message ? { message: result.message } : {}),
|
|
190
|
-
...(result.details !== undefined ? { data: result.details } : {}),
|
|
191
|
-
...(result.success ? {} : { error: result.message || "install failed" }),
|
|
192
|
-
};
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
configure: {
|
|
196
|
-
allowWhenDisabled: true,
|
|
197
|
-
execute: async ({ context, payload }) => {
|
|
198
|
-
const payloadObject = payload && typeof payload === "object" && !Array.isArray(payload)
|
|
199
|
-
? payload
|
|
200
|
-
: {};
|
|
201
|
-
const { enabled: _ignoredEnabled, ...patch } = payloadObject;
|
|
202
|
-
const current = readVoicePluginConfig(context);
|
|
203
|
-
const next = {
|
|
204
|
-
...current,
|
|
205
|
-
...patch,
|
|
206
|
-
};
|
|
207
|
-
await writeVoicePluginConfig({
|
|
208
|
-
agentState: context,
|
|
209
|
-
value: next,
|
|
210
|
-
});
|
|
211
|
-
return {
|
|
212
|
-
success: true,
|
|
213
|
-
data: {
|
|
214
|
-
plugin: toJsonObject(next) || {},
|
|
215
|
-
},
|
|
216
|
-
};
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
on: {
|
|
220
|
-
allowWhenDisabled: true,
|
|
221
|
-
command: {
|
|
222
|
-
description: "启用 asr plugin,并可选安装转写依赖",
|
|
223
|
-
configure(command) {
|
|
224
|
-
command
|
|
225
|
-
.argument("[models...]")
|
|
226
|
-
.option("--active-model <modelId>", "安装完成后设为当前模型")
|
|
227
|
-
.option("--models-dir <path>", "模型目录(可选)")
|
|
228
|
-
.option("--python <bin>", "Python 可执行文件(默认 python3)")
|
|
229
|
-
.option("--no-install", "仅启用 plugin,不安装依赖")
|
|
230
|
-
.option("--no-install-deps", "跳过依赖安装")
|
|
231
|
-
.option("--force", "强制覆盖已存在资源")
|
|
232
|
-
.option("--hf-token <token>", "HuggingFace token(可选)")
|
|
233
|
-
.option("--no-inject-prompt", "关闭 prompt 注入")
|
|
234
|
-
.option("--no-augment-message", "关闭消息增强");
|
|
235
|
-
},
|
|
236
|
-
mapInput({ args, opts }) {
|
|
237
|
-
return {
|
|
238
|
-
modelIds: args,
|
|
239
|
-
...(getStringOpt(opts, "activeModel")
|
|
240
|
-
? { activeModel: getStringOpt(opts, "activeModel") }
|
|
241
|
-
: {}),
|
|
242
|
-
...(getStringOpt(opts, "modelsDir")
|
|
243
|
-
? { modelsDir: getStringOpt(opts, "modelsDir") }
|
|
244
|
-
: {}),
|
|
245
|
-
...(getStringOpt(opts, "python")
|
|
246
|
-
? { pythonBin: getStringOpt(opts, "python") }
|
|
247
|
-
: {}),
|
|
248
|
-
install: getBooleanOpt(opts, "install", true),
|
|
249
|
-
installDeps: getBooleanOpt(opts, "installDeps", true),
|
|
250
|
-
force: getBooleanOpt(opts, "force", false),
|
|
251
|
-
injectPrompt: getBooleanOpt(opts, "injectPrompt", true),
|
|
252
|
-
augmentMessage: getBooleanOpt(opts, "augmentMessage", true),
|
|
253
|
-
...(getStringOpt(opts, "hfToken")
|
|
254
|
-
? { hfToken: getStringOpt(opts, "hfToken") }
|
|
255
|
-
: {}),
|
|
256
|
-
};
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
execute: async ({ context, payload }) => {
|
|
260
|
-
await writeProjectPluginEnabled({
|
|
261
|
-
pluginName: "asr",
|
|
262
|
-
enabled: true,
|
|
263
|
-
context,
|
|
264
|
-
});
|
|
265
|
-
const nextConfig = {
|
|
266
|
-
...readVoicePluginConfig(context),
|
|
267
|
-
injectPrompt: typeof payload.injectPrompt === "boolean"
|
|
268
|
-
? payload.injectPrompt
|
|
269
|
-
: true,
|
|
270
|
-
augmentMessage: typeof payload.augmentMessage === "boolean"
|
|
271
|
-
? payload.augmentMessage
|
|
272
|
-
: true,
|
|
273
|
-
};
|
|
274
|
-
await writeVoicePluginConfig({
|
|
275
|
-
agentState: context,
|
|
276
|
-
value: nextConfig,
|
|
277
|
-
});
|
|
278
|
-
if (payload.install !== false) {
|
|
279
|
-
const installResult = await installVoiceTranscriber({
|
|
280
|
-
context,
|
|
281
|
-
input: payload && typeof payload === "object" && !Array.isArray(payload)
|
|
282
|
-
? payload
|
|
283
|
-
: undefined,
|
|
284
|
-
});
|
|
285
|
-
if (!installResult.success) {
|
|
286
|
-
return {
|
|
287
|
-
success: false,
|
|
288
|
-
error: installResult.message || "asr dependency install failed",
|
|
289
|
-
message: installResult.message || "asr dependency install failed",
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return {
|
|
294
|
-
success: true,
|
|
295
|
-
data: {
|
|
296
|
-
plugin: toJsonObject(nextConfig) || {},
|
|
297
|
-
transcriber: toJsonObject(readVoiceTranscriberConfig(context)) || {},
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
},
|
|
301
|
-
},
|
|
302
|
-
off: {
|
|
303
|
-
command: {
|
|
304
|
-
description: "关闭 asr plugin",
|
|
305
|
-
mapInput() {
|
|
306
|
-
return {};
|
|
307
|
-
},
|
|
308
|
-
},
|
|
309
|
-
execute: async ({ context }) => {
|
|
310
|
-
await writeProjectPluginEnabled({
|
|
311
|
-
pluginName: "asr",
|
|
312
|
-
enabled: false,
|
|
313
|
-
context,
|
|
314
|
-
});
|
|
315
|
-
return {
|
|
316
|
-
success: true,
|
|
317
|
-
data: {
|
|
318
|
-
plugin: toJsonObject(readVoicePluginConfig(context)) || {},
|
|
319
|
-
},
|
|
320
|
-
};
|
|
321
|
-
},
|
|
322
|
-
},
|
|
323
|
-
use: {
|
|
324
|
-
command: {
|
|
325
|
-
description: "切换 asr 当前转写模型",
|
|
326
|
-
configure(command) {
|
|
327
|
-
command.argument("<modelId>");
|
|
328
|
-
},
|
|
329
|
-
mapInput({ args }) {
|
|
330
|
-
const modelId = String(args[0] || "").trim();
|
|
331
|
-
if (!modelId) {
|
|
332
|
-
throw new Error("modelId is required");
|
|
333
|
-
}
|
|
334
|
-
return {
|
|
335
|
-
modelId,
|
|
336
|
-
};
|
|
337
|
-
},
|
|
338
|
-
},
|
|
339
|
-
execute: async ({ context, payload }) => {
|
|
340
|
-
const modelId = String(payload.modelId || "").trim();
|
|
341
|
-
const resolvedModelId = resolveVoicePluginModelId(modelId);
|
|
342
|
-
if (!resolvedModelId) {
|
|
343
|
-
return {
|
|
344
|
-
success: false,
|
|
345
|
-
error: `Unsupported asr model: ${modelId}`,
|
|
346
|
-
message: `Unsupported asr model: ${modelId}`,
|
|
347
|
-
};
|
|
348
|
-
}
|
|
349
|
-
const transcriberConfig = await writeVoiceTranscriberConfig({
|
|
350
|
-
context,
|
|
351
|
-
value: {
|
|
352
|
-
...readVoiceTranscriberConfig(context),
|
|
353
|
-
modelId: resolvedModelId,
|
|
354
|
-
},
|
|
355
|
-
});
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* 显式 action 集合。
|
|
225
|
+
*/
|
|
226
|
+
actions = {
|
|
227
|
+
transcribe: {
|
|
228
|
+
execute: async ({ payload }) => {
|
|
229
|
+
try {
|
|
230
|
+
const input = normalize_asr_payload(payload);
|
|
231
|
+
const result = await this.transcribe(input);
|
|
356
232
|
return {
|
|
357
233
|
success: true,
|
|
358
|
-
data:
|
|
359
|
-
|
|
360
|
-
},
|
|
361
|
-
};
|
|
362
|
-
},
|
|
363
|
-
},
|
|
364
|
-
transcribe: {
|
|
365
|
-
command: {
|
|
366
|
-
description: "转写本地音频文件",
|
|
367
|
-
configure(command) {
|
|
368
|
-
command
|
|
369
|
-
.argument("<audioPath>")
|
|
370
|
-
.option("--language <code>", "语言提示(可选,例如 zh / en)");
|
|
371
|
-
},
|
|
372
|
-
mapInput({ args, opts }) {
|
|
373
|
-
const audioPath = String(args[0] || "").trim();
|
|
374
|
-
if (!audioPath) {
|
|
375
|
-
throw new Error("audioPath is required");
|
|
376
|
-
}
|
|
377
|
-
return {
|
|
378
|
-
audioPath,
|
|
379
|
-
...(getStringOpt(opts, "language")
|
|
380
|
-
? { language: getStringOpt(opts, "language") }
|
|
381
|
-
: {}),
|
|
382
|
-
};
|
|
383
|
-
},
|
|
384
|
-
},
|
|
385
|
-
execute: async ({ context, payload }) => {
|
|
386
|
-
const pluginStatus = await plugin.availability(context);
|
|
387
|
-
if (!pluginStatus.enabled || !pluginStatus.available) {
|
|
388
|
-
return {
|
|
389
|
-
success: false,
|
|
390
|
-
error: pluginStatus.reasons.join("; "),
|
|
391
|
-
message: pluginStatus.reasons.join("; "),
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
const result = await transcribeWithVoiceDependency({
|
|
395
|
-
context,
|
|
396
|
-
audioPath: String(payload.audioPath || ""),
|
|
397
|
-
language: typeof payload.language === "string"
|
|
398
|
-
? String(payload.language || "")
|
|
399
|
-
: undefined,
|
|
400
|
-
});
|
|
401
|
-
return {
|
|
402
|
-
success: result.success,
|
|
403
|
-
...(result !== undefined ? { data: result } : {}),
|
|
404
|
-
...(result.success ? {} : { error: result.error || "transcribe failed" }),
|
|
405
|
-
...(result.success ? {} : { message: result.error || "transcribe failed" }),
|
|
234
|
+
data: result,
|
|
235
|
+
message: "audio transcribed",
|
|
406
236
|
};
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
models: {
|
|
410
|
-
allowWhenDisabled: true,
|
|
411
|
-
command: {
|
|
412
|
-
description: "列出内置 asr 支持的模型目录",
|
|
413
|
-
mapInput() {
|
|
414
|
-
return {};
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
execute: async () => {
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
418
239
|
return {
|
|
419
|
-
success:
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
},
|
|
240
|
+
success: false,
|
|
241
|
+
error: String(error),
|
|
242
|
+
message: String(error),
|
|
423
243
|
};
|
|
424
|
-
}
|
|
244
|
+
}
|
|
425
245
|
},
|
|
426
|
-
doctor: {
|
|
427
|
-
command: {
|
|
428
|
-
description: "检查 asr plugin 与转写依赖可用性",
|
|
429
|
-
mapInput() {
|
|
430
|
-
return {};
|
|
431
|
-
},
|
|
432
|
-
},
|
|
433
|
-
execute: async ({ context }) => {
|
|
434
|
-
const availability = await plugin.availability(context);
|
|
435
|
-
const dependencyStatus = await checkVoiceTranscriber(context);
|
|
436
|
-
return {
|
|
437
|
-
success: true,
|
|
438
|
-
data: {
|
|
439
|
-
availability: {
|
|
440
|
-
enabled: availability.enabled,
|
|
441
|
-
available: availability.available,
|
|
442
|
-
reasons: availability.reasons,
|
|
443
|
-
},
|
|
444
|
-
transcriberStatus: {
|
|
445
|
-
available: dependencyStatus.available,
|
|
446
|
-
reasons: dependencyStatus.reasons,
|
|
447
|
-
...(dependencyStatus.details !== undefined
|
|
448
|
-
? { details: dependencyStatus.details }
|
|
449
|
-
: {}),
|
|
450
|
-
},
|
|
451
|
-
},
|
|
452
|
-
message: availability.available
|
|
453
|
-
? "asr plugin is available"
|
|
454
|
-
: availability.reasons.join("; ") || "asr plugin is not available",
|
|
455
|
-
};
|
|
456
|
-
},
|
|
457
|
-
},
|
|
458
|
-
},
|
|
459
|
-
system(context) {
|
|
460
|
-
const config = readVoicePluginConfig(context);
|
|
461
|
-
if (!isPluginEnabled({ plugin, context }) || config.injectPrompt !== true) {
|
|
462
|
-
return "";
|
|
463
|
-
}
|
|
464
|
-
return [
|
|
465
|
-
"# ASR Plugin",
|
|
466
|
-
"Audio attachments may be transcribed before agent execution.",
|
|
467
|
-
].join("\n");
|
|
468
246
|
},
|
|
469
247
|
};
|
|
470
248
|
}
|
|
471
|
-
/**
|
|
472
|
-
* AsrPlugin:语音识别插件。
|
|
473
|
-
*/
|
|
474
|
-
export class AsrPlugin extends BasePlugin {
|
|
475
|
-
name = "asr";
|
|
476
|
-
constructor() {
|
|
477
|
-
super();
|
|
478
|
-
Object.assign(this, createAsrPluginDefinition(this));
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
249
|
//# sourceMappingURL=Plugin.js.map
|