@siact/sime-x-vue 0.0.9 → 0.0.11
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/sime-x-vue.mjs +874 -964
- package/dist/sime-x-vue.mjs.map +1 -1
- package/dist/sime-x-vue.umd.js +877 -966
- package/dist/sime-x-vue.umd.js.map +1 -1
- package/dist/style.css +373 -797
- package/package.json +6 -5
- package/types/DevApp.vue.d.ts +2 -2
- package/types/components/ai-chat.vue.d.ts +47 -0
- package/types/components/sime-provider.vue.d.ts +26 -49
- package/types/components/test.vue.d.ts +2 -2
- package/types/components/tool-card.vue.d.ts +12 -28
- package/types/components/voice-assistant.vue.d.ts +24 -44
- package/types/components/voice-status.vue.d.ts +8 -20
- package/types/composables/index.d.ts +6 -6
- package/types/composables/use-agent-invoke.d.ts +79 -79
- package/types/composables/use-bubble.d.ts +41 -41
- package/types/composables/use-tts.d.ts +18 -18
- package/types/composables/use-voice-recognition.d.ts +28 -28
- package/types/index.d.ts +7 -5
- package/types/injection-key.d.ts +6 -6
- package/types/lib/agent-chat-transport.d.ts +35 -0
- package/types/lib/data-stream-parser.d.ts +84 -84
- package/types/lib/utils.d.ts +14 -14
- package/types/main.d.ts +1 -1
- package/types/types.d.ts +45 -54
- package/types/utils/command-manager.d.ts +16 -0
- package/types/utils/command-types.d.ts +20 -0
- package/types/utils/index.d.ts +2 -0
- package/types/components/execution-status.vue.d.ts +0 -17
- package/types/components/sime-x.vue.d.ts +0 -43
- package/types/debug-page.vue.d.ts +0 -2
package/dist/sime-x-vue.mjs
CHANGED
|
@@ -1,999 +1,809 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { defineComponent, ref, reactive, computed, watch, onMounted, openBlock, createElementBlock, normalizeClass, createElementVNode, toDisplayString, withModifiers, withDirectives, vModelText, Fragment, renderList, createCommentVNode, unref, createVNode, Transition, withCtx, nextTick, inject, shallowRef, provide, renderSlot, onBeforeUnmount, normalizeStyle } from 'vue';
|
|
2
|
+
import { Chat } from '@ai-sdk/vue';
|
|
3
|
+
import { DefaultChatTransport } from 'ai';
|
|
4
|
+
import { SpeechSynthesizerStandalone, WakeWordDetectorStandalone, SpeechTranscriberStandalone } from 'web-voice-kit';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
6
|
+
function createAgentChatTransport(options) {
|
|
7
|
+
const { api, projectId, getCommands, headers: extraHeaders, body: extraBody } = options;
|
|
8
|
+
return new DefaultChatTransport({
|
|
9
|
+
api,
|
|
10
|
+
headers: extraHeaders,
|
|
11
|
+
prepareSendMessagesRequest({ messages }) {
|
|
12
|
+
const lastUserMessage = findLastUserMessage(messages);
|
|
13
|
+
const input = lastUserMessage ? lastUserMessage.parts.filter((p) => p.type === "text").map((p) => p.text).join("") : "";
|
|
14
|
+
const historyMessages = buildHistoryMessages(messages, lastUserMessage?.id);
|
|
15
|
+
const resolvedExtraBody = typeof extraBody === "function" ? extraBody() : extraBody || {};
|
|
16
|
+
return {
|
|
17
|
+
body: {
|
|
18
|
+
input,
|
|
19
|
+
projectId: projectId || "",
|
|
20
|
+
messages: historyMessages.length > 0 ? historyMessages : void 0,
|
|
21
|
+
...resolvedExtraBody
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
});
|
|
19
26
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
appToken: {},
|
|
41
|
-
voiceConfig: {}
|
|
42
|
-
},
|
|
43
|
-
setup(__props) {
|
|
44
|
-
const props = __props;
|
|
45
|
-
const hostBridge = shallowRef(new HostBridge({ debug: false }));
|
|
46
|
-
const startListeningRef = shallowRef(async () => {
|
|
47
|
-
});
|
|
48
|
-
const stopListeningRef = shallowRef(async () => {
|
|
49
|
-
});
|
|
50
|
-
const toggleCollapseRef = shallowRef(async () => {
|
|
51
|
-
});
|
|
52
|
-
const openDialogRef = shallowRef(async () => {
|
|
53
|
-
});
|
|
54
|
-
const closeDialogRef = shallowRef(async () => {
|
|
55
|
-
});
|
|
56
|
-
provide(AiChatbotXKey, {
|
|
57
|
-
chatbotUrl: () => props.chatbotUrl,
|
|
58
|
-
appId: () => props.appId,
|
|
59
|
-
appToken: () => props.appToken,
|
|
60
|
-
voiceConfig: () => props.voiceConfig || { appId: "", apiKey: "", websocketUrl: "" },
|
|
61
|
-
startListening: () => startListeningRef.value(),
|
|
62
|
-
stopListening: () => stopListeningRef.value(),
|
|
63
|
-
toggleCollapse: () => toggleCollapseRef.value(),
|
|
64
|
-
openDialog: () => openDialogRef.value(),
|
|
65
|
-
closeDialog: () => closeDialogRef.value(),
|
|
66
|
-
registerVoiceMethods: (methods) => {
|
|
67
|
-
startListeningRef.value = methods.start;
|
|
68
|
-
stopListeningRef.value = methods.stop;
|
|
69
|
-
toggleCollapseRef.value = methods.toggleCollapse;
|
|
70
|
-
openDialogRef.value = methods.openDialog;
|
|
71
|
-
closeDialogRef.value = methods.closeDialog;
|
|
72
|
-
},
|
|
73
|
-
clientCommand: () => hostBridge.value.clientCommands(),
|
|
74
|
-
hostCommads: () => hostBridge.value.hostCommands(),
|
|
75
|
-
registerCommand: (cmd) => {
|
|
76
|
-
hostBridge.value.registerCommand(cmd);
|
|
77
|
-
},
|
|
78
|
-
unregisterCommand: (name) => {
|
|
79
|
-
hostBridge.value.unregisterCommand(name);
|
|
80
|
-
},
|
|
81
|
-
async appendMessage(message) {
|
|
82
|
-
await hostBridge.value.executeClientCommand(clientCommandKey.APPEND_MESSAGE, [message]);
|
|
83
|
-
},
|
|
84
|
-
async setTheme(theme) {
|
|
85
|
-
await hostBridge.value.executeClientCommand(clientCommandKey.SET_THEME, [theme]);
|
|
86
|
-
},
|
|
87
|
-
async weak() {
|
|
88
|
-
await hostBridge.value.executeClientCommand(clientCommandKey.WAKE);
|
|
89
|
-
},
|
|
90
|
-
async startNewConversation() {
|
|
91
|
-
await hostBridge.value.executeClientCommand(clientCommandKey.START_NEW_CONVERSATION);
|
|
92
|
-
},
|
|
93
|
-
setIframeElement(iframe) {
|
|
94
|
-
hostBridge.value.setIframe(iframe);
|
|
95
|
-
},
|
|
96
|
-
async recognition(message, commands) {
|
|
97
|
-
return await hostBridge.value.executeClientCommand(clientCommandKey.RECOGNITION, [message, commands]);
|
|
98
|
-
},
|
|
99
|
-
async executeCommand(commandName, args = []) {
|
|
100
|
-
return await hostBridge.value.executeCommand(commandName, args);
|
|
27
|
+
class AgentChatTransport extends DefaultChatTransport {
|
|
28
|
+
agentOptions;
|
|
29
|
+
constructor(options) {
|
|
30
|
+
const { api, headers: extraHeaders, body: extraBody, projectId } = options;
|
|
31
|
+
super({
|
|
32
|
+
api,
|
|
33
|
+
headers: extraHeaders,
|
|
34
|
+
prepareSendMessagesRequest({ messages }) {
|
|
35
|
+
const lastUserMessage = findLastUserMessage(messages);
|
|
36
|
+
const input = lastUserMessage ? lastUserMessage.parts.filter((p) => p.type === "text").map((p) => p.text).join("") : "";
|
|
37
|
+
const historyMessages = buildHistoryMessages(messages, lastUserMessage?.id);
|
|
38
|
+
const resolvedExtraBody = typeof extraBody === "function" ? extraBody() : extraBody || {};
|
|
39
|
+
return {
|
|
40
|
+
body: {
|
|
41
|
+
input,
|
|
42
|
+
projectId: projectId || "",
|
|
43
|
+
messages: historyMessages.length > 0 ? historyMessages : void 0,
|
|
44
|
+
...resolvedExtraBody
|
|
45
|
+
}
|
|
46
|
+
};
|
|
101
47
|
}
|
|
102
48
|
});
|
|
103
|
-
|
|
104
|
-
return renderSlot(_ctx.$slots, "default");
|
|
105
|
-
};
|
|
49
|
+
this.agentOptions = options;
|
|
106
50
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const _hoisted_1$3 = { class: "content-container" };
|
|
110
|
-
const _hoisted_2$3 = { class: "status-header" };
|
|
111
|
-
const _hoisted_3$2 = { class: "status-text" };
|
|
112
|
-
const _hoisted_4$2 = {
|
|
113
|
-
key: 0,
|
|
114
|
-
class: "transcription-content"
|
|
115
|
-
};
|
|
116
|
-
const _hoisted_5$2 = {
|
|
117
|
-
key: 1,
|
|
118
|
-
class: "placeholder-text"
|
|
119
|
-
};
|
|
120
|
-
const _sfc_main$3 = /* @__PURE__ */ defineComponent({
|
|
121
|
-
__name: "voice-status",
|
|
122
|
-
props: {
|
|
123
|
-
status: {},
|
|
124
|
-
transcriptionText: {},
|
|
125
|
-
isTranscribing: { type: Boolean }
|
|
126
|
-
},
|
|
127
|
-
setup(__props) {
|
|
128
|
-
const props = __props;
|
|
129
|
-
const currentMode = computed(() => {
|
|
130
|
-
if (props.isTranscribing) return "mode-transcribing";
|
|
131
|
-
if (props.status === "wake") return "mode-wake";
|
|
132
|
-
return "mode-standby";
|
|
133
|
-
});
|
|
134
|
-
const statusLabel = computed(() => {
|
|
135
|
-
if (props.isTranscribing) return "正在聆听您的问题...";
|
|
136
|
-
if (props.status === "wake") return "您好,有什么可以帮助您的吗?";
|
|
137
|
-
return "Standby";
|
|
138
|
-
});
|
|
139
|
-
return (_ctx, _cache) => {
|
|
140
|
-
return openBlock(), createBlock(Transition, { name: "voice-panel" }, {
|
|
141
|
-
default: withCtx(() => [
|
|
142
|
-
__props.status === "wake" || __props.isTranscribing ? (openBlock(), createElementBlock("div", {
|
|
143
|
-
key: 0,
|
|
144
|
-
class: normalizeClass(["voice-status-wrapper", currentMode.value])
|
|
145
|
-
}, [
|
|
146
|
-
_cache[0] || (_cache[0] = createElementVNode("div", { class: "indicator-container" }, [
|
|
147
|
-
createElementVNode("div", { class: "ambient-glow" }),
|
|
148
|
-
createElementVNode("div", { class: "ripple-layer" }, [
|
|
149
|
-
createElementVNode("div", { class: "ripple delay-1" }),
|
|
150
|
-
createElementVNode("div", { class: "ripple delay-2" }),
|
|
151
|
-
createElementVNode("div", { class: "ripple delay-3" })
|
|
152
|
-
]),
|
|
153
|
-
createElementVNode("div", { class: "icon-core" }, [
|
|
154
|
-
createElementVNode("svg", {
|
|
155
|
-
class: "mic-icon",
|
|
156
|
-
viewBox: "0 0 24 24",
|
|
157
|
-
fill: "none",
|
|
158
|
-
stroke: "currentColor",
|
|
159
|
-
"stroke-width": "2",
|
|
160
|
-
"stroke-linecap": "round",
|
|
161
|
-
"stroke-linejoin": "round"
|
|
162
|
-
}, [
|
|
163
|
-
createElementVNode("rect", {
|
|
164
|
-
x: "9",
|
|
165
|
-
y: "2",
|
|
166
|
-
width: "6",
|
|
167
|
-
height: "12",
|
|
168
|
-
rx: "3",
|
|
169
|
-
class: "mic-capsule"
|
|
170
|
-
}),
|
|
171
|
-
createElementVNode("path", {
|
|
172
|
-
d: "M5 10C5 13.866 8.13401 17 12 17C15.866 17 19 13.866 19 10",
|
|
173
|
-
class: "mic-stand"
|
|
174
|
-
}),
|
|
175
|
-
createElementVNode("path", {
|
|
176
|
-
d: "M12 17V21M8 21H16",
|
|
177
|
-
class: "mic-base"
|
|
178
|
-
})
|
|
179
|
-
])
|
|
180
|
-
])
|
|
181
|
-
], -1)),
|
|
182
|
-
createElementVNode("div", _hoisted_1$3, [
|
|
183
|
-
createElementVNode("div", _hoisted_2$3, [
|
|
184
|
-
createElementVNode("span", _hoisted_3$2, toDisplayString(statusLabel.value), 1)
|
|
185
|
-
]),
|
|
186
|
-
createElementVNode("div", {
|
|
187
|
-
class: normalizeClass(["text-window", { "has-text": !!__props.transcriptionText }])
|
|
188
|
-
}, [
|
|
189
|
-
createVNode(Transition, {
|
|
190
|
-
name: "fade-slide",
|
|
191
|
-
mode: "out-in"
|
|
192
|
-
}, {
|
|
193
|
-
default: withCtx(() => [
|
|
194
|
-
__props.transcriptionText ? (openBlock(), createElementBlock("p", _hoisted_4$2, toDisplayString(__props.transcriptionText), 1)) : __props.status === "wake" ? (openBlock(), createElementBlock("p", _hoisted_5$2, "Listening...")) : createCommentVNode("", true)
|
|
195
|
-
]),
|
|
196
|
-
_: 1
|
|
197
|
-
})
|
|
198
|
-
], 2)
|
|
199
|
-
])
|
|
200
|
-
], 2)) : createCommentVNode("", true)
|
|
201
|
-
]),
|
|
202
|
-
_: 1
|
|
203
|
-
});
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const _export_sfc = (sfc, props) => {
|
|
209
|
-
const target = sfc.__vccOpts || sfc;
|
|
210
|
-
for (const [key, val] of props) {
|
|
211
|
-
target[key] = val;
|
|
212
|
-
}
|
|
213
|
-
return target;
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
const VoiceStatus = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-c9fa6caf"]]);
|
|
217
|
-
|
|
218
|
-
const _hoisted_1$2 = {
|
|
219
|
-
key: 0,
|
|
220
|
-
class: "execution-bubble"
|
|
221
|
-
};
|
|
222
|
-
const _hoisted_2$2 = { class: "exec-text" };
|
|
223
|
-
const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
224
|
-
__name: "execution-status",
|
|
225
|
-
props: {
|
|
226
|
-
visible: { type: Boolean },
|
|
227
|
-
text: {}
|
|
228
|
-
},
|
|
229
|
-
setup(__props) {
|
|
230
|
-
return (_ctx, _cache) => {
|
|
231
|
-
return openBlock(), createBlock(Transition, { name: "exec-bubble" }, {
|
|
232
|
-
default: withCtx(() => [
|
|
233
|
-
__props.visible ? (openBlock(), createElementBlock("div", _hoisted_1$2, [
|
|
234
|
-
createElementVNode("span", _hoisted_2$2, toDisplayString(__props.text || "执行中"), 1),
|
|
235
|
-
_cache[0] || (_cache[0] = createElementVNode("div", { class: "loading-dots" }, [
|
|
236
|
-
createElementVNode("span", { class: "dot" }),
|
|
237
|
-
createElementVNode("span", { class: "dot" }),
|
|
238
|
-
createElementVNode("span", { class: "dot" })
|
|
239
|
-
], -1))
|
|
240
|
-
])) : createCommentVNode("", true)
|
|
241
|
-
]),
|
|
242
|
-
_: 1
|
|
243
|
-
});
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
const ExecutionStatus = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-8244ff0d"]]);
|
|
249
|
-
|
|
250
|
-
const ensureMicrophonePermission = async () => {
|
|
251
|
-
if (typeof navigator === "undefined" || typeof window === "undefined") {
|
|
252
|
-
console.log("当前环境不支持麦克风访问");
|
|
253
|
-
return false;
|
|
254
|
-
}
|
|
255
|
-
if (!navigator.mediaDevices?.getUserMedia || !navigator.mediaDevices?.enumerateDevices) {
|
|
256
|
-
console.log("当前环境不支持麦克风访问");
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
259
|
-
try {
|
|
260
|
-
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
261
|
-
const audioInputDevices = devices.filter((device) => device.kind === "audioinput");
|
|
262
|
-
if (audioInputDevices.length === 0) {
|
|
263
|
-
console.log("未检测到麦克风设备,请连接麦克风后重试。");
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
if ("permissions" in navigator && navigator.permissions?.query) {
|
|
51
|
+
async sendMessages(options) {
|
|
52
|
+
if (this.agentOptions.getCommands) {
|
|
267
53
|
try {
|
|
268
|
-
const
|
|
269
|
-
if (
|
|
270
|
-
|
|
271
|
-
|
|
54
|
+
const commands = await this.agentOptions.getCommands();
|
|
55
|
+
if (commands && commands.length > 0) {
|
|
56
|
+
options.body = {
|
|
57
|
+
...options.body || {},
|
|
58
|
+
commands
|
|
59
|
+
};
|
|
272
60
|
}
|
|
273
|
-
} catch
|
|
274
|
-
console.warn("Permission query not supported:", e);
|
|
61
|
+
} catch {
|
|
275
62
|
}
|
|
276
63
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
const audioTracks = stream.getAudioTracks();
|
|
287
|
-
if (audioTracks.length === 0) {
|
|
288
|
-
console.log("无法获取麦克风音频轨道。");
|
|
289
|
-
return false;
|
|
290
|
-
}
|
|
291
|
-
const activeTrack = audioTracks[0];
|
|
292
|
-
if (!activeTrack.enabled || activeTrack.readyState !== "live") {
|
|
293
|
-
console.log("麦克风设备不可用,请检查设备连接。");
|
|
294
|
-
return false;
|
|
295
|
-
}
|
|
296
|
-
return true;
|
|
297
|
-
} finally {
|
|
298
|
-
if (stream) {
|
|
299
|
-
stream.getTracks().forEach((track) => track.stop());
|
|
300
|
-
}
|
|
64
|
+
return super.sendMessages(options);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function findLastUserMessage(messages) {
|
|
68
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
69
|
+
if (messages[i].role === "user") {
|
|
70
|
+
return messages[i];
|
|
301
71
|
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
72
|
+
}
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
function buildHistoryMessages(messages, excludeId) {
|
|
76
|
+
const history = [];
|
|
77
|
+
for (const msg of messages) {
|
|
78
|
+
if (msg.id === excludeId) continue;
|
|
79
|
+
if (msg.role !== "user" && msg.role !== "assistant") continue;
|
|
80
|
+
const textContent = msg.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
81
|
+
if (textContent.trim()) {
|
|
82
|
+
history.push({
|
|
83
|
+
role: msg.role,
|
|
84
|
+
content: textContent
|
|
85
|
+
});
|
|
312
86
|
}
|
|
313
|
-
return false;
|
|
314
87
|
}
|
|
315
|
-
|
|
88
|
+
return history;
|
|
89
|
+
}
|
|
316
90
|
|
|
317
|
-
const _hoisted_1$1 =
|
|
318
|
-
const _hoisted_2$1 = { class: "fab-avatar-wrapper" };
|
|
319
|
-
const _hoisted_3$1 = ["src"];
|
|
320
|
-
const _hoisted_4$1 = { class: "header-left" };
|
|
321
|
-
const _hoisted_5$1 = { class: "logo-icon" };
|
|
322
|
-
const _hoisted_6$1 = ["src"];
|
|
323
|
-
const _hoisted_7$1 = { class: "title" };
|
|
324
|
-
const _hoisted_8$1 = { class: "actions" };
|
|
325
|
-
const _hoisted_9$1 = ["title"];
|
|
326
|
-
const _hoisted_10$1 = {
|
|
91
|
+
const _hoisted_1$1 = {
|
|
327
92
|
key: 0,
|
|
328
|
-
class: "
|
|
93
|
+
class: "ai-chat__welcome"
|
|
329
94
|
};
|
|
330
|
-
const
|
|
331
|
-
const
|
|
95
|
+
const _hoisted_2$1 = { class: "ai-chat__welcome-header" };
|
|
96
|
+
const _hoisted_3$1 = { class: "ai-chat__welcome-title" };
|
|
97
|
+
const _hoisted_4$1 = { class: "ai-chat__welcome-desc" };
|
|
98
|
+
const _hoisted_5$1 = { class: "ai-chat__input-area" };
|
|
99
|
+
const _hoisted_6$1 = { class: "ai-chat__input-wrapper" };
|
|
100
|
+
const _hoisted_7$1 = ["disabled"];
|
|
101
|
+
const _hoisted_8$1 = {
|
|
102
|
+
key: 0,
|
|
103
|
+
class: "ai-chat__suggestions"
|
|
104
|
+
};
|
|
105
|
+
const _hoisted_9$1 = ["onClick"];
|
|
106
|
+
const _hoisted_10$1 = { class: "ai-chat__messages-inner" };
|
|
107
|
+
const _hoisted_11$1 = { class: "ai-chat__message-content" };
|
|
108
|
+
const _hoisted_12$1 = ["innerHTML"];
|
|
332
109
|
const _hoisted_13$1 = {
|
|
333
|
-
|
|
334
|
-
|
|
110
|
+
key: 1,
|
|
111
|
+
class: "ai-chat__reasoning"
|
|
112
|
+
};
|
|
113
|
+
const _hoisted_14 = ["onClick"];
|
|
114
|
+
const _hoisted_15 = {
|
|
115
|
+
key: 0,
|
|
116
|
+
class: "ai-chat__reasoning-streaming"
|
|
117
|
+
};
|
|
118
|
+
const _hoisted_16 = {
|
|
119
|
+
key: 0,
|
|
120
|
+
class: "ai-chat__reasoning-content"
|
|
121
|
+
};
|
|
122
|
+
const _hoisted_17 = {
|
|
123
|
+
key: 2,
|
|
124
|
+
class: "ai-chat__tool"
|
|
125
|
+
};
|
|
126
|
+
const _hoisted_18 = { class: "ai-chat__tool-icon" };
|
|
127
|
+
const _hoisted_19 = {
|
|
128
|
+
key: 0,
|
|
129
|
+
class: "ai-chat__tool-spinner",
|
|
130
|
+
width: "14",
|
|
131
|
+
height: "14",
|
|
335
132
|
viewBox: "0 0 24 24",
|
|
336
133
|
fill: "none"
|
|
337
134
|
};
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
135
|
+
const _hoisted_20 = {
|
|
136
|
+
key: 1,
|
|
137
|
+
width: "14",
|
|
138
|
+
height: "14",
|
|
139
|
+
viewBox: "0 0 24 24",
|
|
140
|
+
fill: "none"
|
|
141
|
+
};
|
|
142
|
+
const _hoisted_21 = {
|
|
143
|
+
key: 2,
|
|
144
|
+
width: "14",
|
|
145
|
+
height: "14",
|
|
146
|
+
viewBox: "0 0 24 24",
|
|
147
|
+
fill: "none"
|
|
148
|
+
};
|
|
149
|
+
const _hoisted_22 = { class: "ai-chat__tool-name" };
|
|
150
|
+
const _hoisted_23 = {
|
|
151
|
+
key: 0,
|
|
152
|
+
class: "ai-chat__message-actions"
|
|
153
|
+
};
|
|
154
|
+
const _hoisted_24 = ["onClick"];
|
|
155
|
+
const _hoisted_25 = ["onClick"];
|
|
156
|
+
const _hoisted_26 = {
|
|
157
|
+
key: 0,
|
|
158
|
+
width: "12",
|
|
159
|
+
height: "12",
|
|
160
|
+
viewBox: "0 0 24 24",
|
|
161
|
+
fill: "none",
|
|
162
|
+
stroke: "currentColor",
|
|
163
|
+
"stroke-width": "2",
|
|
164
|
+
"stroke-linecap": "round",
|
|
165
|
+
"stroke-linejoin": "round"
|
|
166
|
+
};
|
|
167
|
+
const _hoisted_27 = {
|
|
168
|
+
key: 1,
|
|
169
|
+
width: "12",
|
|
170
|
+
height: "12",
|
|
171
|
+
viewBox: "0 0 24 24",
|
|
172
|
+
fill: "none",
|
|
173
|
+
stroke: "currentColor",
|
|
174
|
+
"stroke-width": "2",
|
|
175
|
+
"stroke-linecap": "round",
|
|
176
|
+
"stroke-linejoin": "round"
|
|
177
|
+
};
|
|
178
|
+
const _hoisted_28 = {
|
|
179
|
+
key: 0,
|
|
180
|
+
class: "ai-chat__thinking"
|
|
181
|
+
};
|
|
182
|
+
const _hoisted_29 = {
|
|
183
|
+
key: 0,
|
|
184
|
+
class: "ai-chat__input-area ai-chat__input-area--bottom"
|
|
185
|
+
};
|
|
186
|
+
const _hoisted_30 = { class: "ai-chat__input-wrapper" };
|
|
187
|
+
const _hoisted_31 = ["disabled"];
|
|
188
|
+
const _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
189
|
+
__name: "ai-chat",
|
|
343
190
|
props: {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
191
|
+
api: {},
|
|
192
|
+
projectId: {},
|
|
193
|
+
isReadonly: { type: Boolean, default: false },
|
|
194
|
+
welcomeTitle: { default: "你好,有什么可以帮助你的吗?" },
|
|
195
|
+
welcomeDescription: { default: "我可以帮你回答问题、分析数据、生成报告等。" },
|
|
196
|
+
suggestions: { default: () => [] },
|
|
197
|
+
fullWidth: { type: Boolean, default: false },
|
|
198
|
+
toolNames: { default: () => ({}) },
|
|
199
|
+
transportOptions: {},
|
|
200
|
+
headers: {},
|
|
201
|
+
body: {}
|
|
351
202
|
},
|
|
352
|
-
emits: ["
|
|
353
|
-
setup(__props, { emit: __emit }) {
|
|
203
|
+
emits: ["error", "finish"],
|
|
204
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
205
|
+
const toolDisplayNames = {
|
|
206
|
+
generateReport: "生成报告",
|
|
207
|
+
searchKnowledge: "知识库检索",
|
|
208
|
+
resolveInstanceTargets: "解析实例目标",
|
|
209
|
+
getHistoryMetrics: "历史数据查询",
|
|
210
|
+
getRealtimeMetrics: "实时数据查询",
|
|
211
|
+
queryBitableData: "多维表格查询",
|
|
212
|
+
searchUser: "搜索用户",
|
|
213
|
+
createBitableRecord: "创建表格记录",
|
|
214
|
+
timeTool: "时间工具",
|
|
215
|
+
loadSkill: "加载技能",
|
|
216
|
+
executeCommand: "执行命令",
|
|
217
|
+
dataAnalyzer: "数据分析",
|
|
218
|
+
dataPredictor: "数据预测"
|
|
219
|
+
};
|
|
354
220
|
const props = __props;
|
|
355
221
|
const emit = __emit;
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const getSystemTheme = () => {
|
|
372
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
373
|
-
};
|
|
374
|
-
const currentTheme = ref(props.xTheme === "system" ? getSystemTheme() : props.xTheme || "light");
|
|
375
|
-
const cycleTheme = async () => {
|
|
376
|
-
currentTheme.value = currentTheme.value === "light" ? "dark" : "light";
|
|
377
|
-
aiChatbotX.setTheme(currentTheme.value);
|
|
378
|
-
};
|
|
379
|
-
const startNewConversation = () => {
|
|
380
|
-
aiChatbotX.startNewConversation();
|
|
381
|
-
};
|
|
382
|
-
const themeTooltip = computed(() => currentTheme.value === "light" ? "切换到深色模式" : "切换到浅色模式");
|
|
383
|
-
const voiceButtonTooltip = computed(() => {
|
|
384
|
-
if (isInitializing.value) return "初始化中...";
|
|
385
|
-
if (initError.value) return `错误: ${initError.value}`;
|
|
386
|
-
switch (voiceStatus.value) {
|
|
387
|
-
case "standby":
|
|
388
|
-
return "开启语音监听";
|
|
389
|
-
case "listening":
|
|
390
|
-
return "监听中,等待唤醒词...";
|
|
391
|
-
case "wake":
|
|
392
|
-
return "已唤醒!";
|
|
393
|
-
default:
|
|
394
|
-
return "语音监听";
|
|
222
|
+
const transport = new AgentChatTransport({
|
|
223
|
+
api: props.api,
|
|
224
|
+
projectId: props.projectId,
|
|
225
|
+
headers: props.headers,
|
|
226
|
+
body: props.body,
|
|
227
|
+
...props.transportOptions
|
|
228
|
+
});
|
|
229
|
+
const chat = new Chat({
|
|
230
|
+
transport,
|
|
231
|
+
onError: (error) => {
|
|
232
|
+
console.error("[AiChat] error:", error);
|
|
233
|
+
emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
234
|
+
},
|
|
235
|
+
onFinish: ({ message }) => {
|
|
236
|
+
emit("finish", message);
|
|
395
237
|
}
|
|
396
238
|
});
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
239
|
+
const inputText = ref("");
|
|
240
|
+
const textareaRef = ref(null);
|
|
241
|
+
const messagesContainerRef = ref(null);
|
|
242
|
+
const showScrollButton = ref(false);
|
|
243
|
+
const copiedId = ref(null);
|
|
244
|
+
const reasoningOpen = reactive({});
|
|
245
|
+
const isEmpty = computed(() => chat.messages.length === 0);
|
|
246
|
+
const isLoading = computed(() => chat.status === "streaming" || chat.status === "submitted");
|
|
247
|
+
const scrollToBottom = () => {
|
|
248
|
+
nextTick(() => {
|
|
249
|
+
const el = messagesContainerRef.value;
|
|
250
|
+
if (el) {
|
|
251
|
+
el.scrollTop = el.scrollHeight;
|
|
252
|
+
}
|
|
405
253
|
});
|
|
406
|
-
}
|
|
407
|
-
const initVoiceDetector = () => {
|
|
408
|
-
if (detector || isInitializing.value) return;
|
|
409
|
-
isInitializing.value = true;
|
|
410
|
-
initError.value = "";
|
|
411
|
-
if (!props.modelPath) {
|
|
412
|
-
initError.value = "未提供语音模型文件";
|
|
413
|
-
isInitializing.value = false;
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
try {
|
|
417
|
-
detector = new WakeWordDetectorStandalone({
|
|
418
|
-
modelPath: props.modelPath,
|
|
419
|
-
sampleRate: 16e3,
|
|
420
|
-
usePartial: true,
|
|
421
|
-
autoReset: {
|
|
422
|
-
enabled: true,
|
|
423
|
-
resetDelayMs: 5e3
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
const wakeWords = props.wakeWords || ["你好", "您好"];
|
|
427
|
-
detector.setWakeWords(wakeWords);
|
|
428
|
-
detector.onWake(() => {
|
|
429
|
-
console.log("[VoiceDetector] 检测到唤醒词");
|
|
430
|
-
wakeAnimating.value = true;
|
|
431
|
-
playWakeResponse();
|
|
432
|
-
emit("wakeUp", true);
|
|
433
|
-
aiChatbotX.weak();
|
|
434
|
-
aiChatbotX.openDialog();
|
|
435
|
-
setTimeout(() => {
|
|
436
|
-
wakeAnimating.value = false;
|
|
437
|
-
}, 1500);
|
|
438
|
-
voiceStatus.value = "listening";
|
|
439
|
-
});
|
|
440
|
-
detector.onError((error) => {
|
|
441
|
-
console.error("[VoiceDetector] 错误:", error);
|
|
442
|
-
initError.value = error.message;
|
|
443
|
-
voiceStatus.value = "standby";
|
|
444
|
-
isTranscribing.value = false;
|
|
445
|
-
if (error.message.includes("permission")) {
|
|
446
|
-
transcriptionText.value = "需要麦克风权限";
|
|
447
|
-
} else if (error.message.includes("model")) {
|
|
448
|
-
transcriptionText.value = "模型加载失败";
|
|
449
|
-
} else {
|
|
450
|
-
transcriptionText.value = "初始化失败";
|
|
451
|
-
}
|
|
452
|
-
setTimeout(() => {
|
|
453
|
-
transcriptionText.value = "";
|
|
454
|
-
}, 3e3);
|
|
455
|
-
});
|
|
456
|
-
console.log("[VoiceDetector] 初始化成功");
|
|
457
|
-
} catch (error) {
|
|
458
|
-
console.error("[VoiceDetector] 初始化失败:", error);
|
|
459
|
-
initError.value = error instanceof Error ? error.message : "初始化失败";
|
|
460
|
-
voiceStatus.value = "standby";
|
|
461
|
-
isTranscribing.value = false;
|
|
462
|
-
} finally {
|
|
463
|
-
isInitializing.value = false;
|
|
464
|
-
}
|
|
465
254
|
};
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
255
|
+
const handleScroll = () => {
|
|
256
|
+
const el = messagesContainerRef.value;
|
|
257
|
+
if (!el) return;
|
|
258
|
+
const threshold = 100;
|
|
259
|
+
showScrollButton.value = el.scrollHeight - el.scrollTop - el.clientHeight > threshold;
|
|
260
|
+
};
|
|
261
|
+
watch(
|
|
262
|
+
() => chat.messages.length,
|
|
263
|
+
() => {
|
|
264
|
+
if (!showScrollButton.value) {
|
|
265
|
+
scrollToBottom();
|
|
471
266
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
watch(
|
|
270
|
+
() => {
|
|
271
|
+
const msgs = chat.messages;
|
|
272
|
+
if (msgs.length === 0) return "";
|
|
273
|
+
const last = msgs[msgs.length - 1];
|
|
274
|
+
return last.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
275
|
+
},
|
|
276
|
+
() => {
|
|
277
|
+
if (!showScrollButton.value) {
|
|
278
|
+
scrollToBottom();
|
|
483
279
|
}
|
|
484
|
-
window.speechSynthesis.speak(utterance);
|
|
485
|
-
} catch (error) {
|
|
486
|
-
console.error("[TTS] 播报失败:", error);
|
|
487
280
|
}
|
|
281
|
+
);
|
|
282
|
+
const autoResize = () => {
|
|
283
|
+
const el = textareaRef.value;
|
|
284
|
+
if (!el) return;
|
|
285
|
+
el.style.height = "auto";
|
|
286
|
+
el.style.height = `${Math.min(el.scrollHeight, 200)}px`;
|
|
488
287
|
};
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
if (!permission) return;
|
|
494
|
-
if (isInitializing.value) return;
|
|
495
|
-
if (!detector) {
|
|
496
|
-
await initVoiceDetector();
|
|
497
|
-
if (!detector) return;
|
|
498
|
-
}
|
|
499
|
-
const isCurrentlyListening = voiceStatus.value === "listening";
|
|
500
|
-
const shouldStart = targetState !== void 0 ? targetState : !isCurrentlyListening;
|
|
501
|
-
if (shouldStart === isCurrentlyListening) return;
|
|
502
|
-
try {
|
|
503
|
-
if (shouldStart) {
|
|
504
|
-
console.log("[VoiceDetector] 强制/自动启动监听...");
|
|
505
|
-
await detector.start();
|
|
506
|
-
voiceStatus.value = "listening";
|
|
507
|
-
transcriptionText.value = "";
|
|
508
|
-
isTranscribing.value = false;
|
|
509
|
-
} else {
|
|
510
|
-
console.log("[VoiceDetector] 强制/自动停止监听...");
|
|
511
|
-
await detector.stop();
|
|
512
|
-
stopTranscribing();
|
|
513
|
-
voiceStatus.value = "standby";
|
|
514
|
-
transcriptionText.value = "";
|
|
515
|
-
isTranscribing.value = false;
|
|
516
|
-
}
|
|
517
|
-
} catch (error) {
|
|
518
|
-
console.error("[VoiceDetector] 操作失败:", error);
|
|
519
|
-
voiceStatus.value = "standby";
|
|
520
|
-
transcriptionText.value = "操作失败";
|
|
521
|
-
isTranscribing.value = false;
|
|
522
|
-
setTimeout(() => {
|
|
523
|
-
transcriptionText.value = "";
|
|
524
|
-
}, 2e3);
|
|
288
|
+
const handleKeydown = (e) => {
|
|
289
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
290
|
+
e.preventDefault();
|
|
291
|
+
handleSubmit();
|
|
525
292
|
}
|
|
526
293
|
};
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
return {
|
|
538
|
-
x: Math.max(margin, Math.min(x, maxX)),
|
|
539
|
-
y: Math.max(margin, Math.min(y, maxY))
|
|
540
|
-
};
|
|
294
|
+
const handleSubmit = () => {
|
|
295
|
+
const text = inputText.value.trim();
|
|
296
|
+
if (!text || isLoading.value) return;
|
|
297
|
+
chat.sendMessage({ text });
|
|
298
|
+
inputText.value = "";
|
|
299
|
+
nextTick(() => {
|
|
300
|
+
if (textareaRef.value) {
|
|
301
|
+
textareaRef.value.style.height = "auto";
|
|
302
|
+
}
|
|
303
|
+
});
|
|
541
304
|
};
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
const overlapY = Math.max(0, Math.min(rectA.bottom, rectB.bottom) - Math.max(rectA.top, rectB.top));
|
|
545
|
-
return overlapX * overlapY;
|
|
305
|
+
const handleSuggestionClick = (suggestion) => {
|
|
306
|
+
chat.sendMessage({ text: suggestion });
|
|
546
307
|
};
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
const fabRect = fabRef.value.getBoundingClientRect();
|
|
550
|
-
const safeFabRect = {
|
|
551
|
-
left: fabRect.left - FAB_SAFE_GAP,
|
|
552
|
-
right: fabRect.right + FAB_SAFE_GAP,
|
|
553
|
-
top: fabRect.top - FAB_SAFE_GAP,
|
|
554
|
-
bottom: fabRect.bottom + FAB_SAFE_GAP
|
|
555
|
-
};
|
|
556
|
-
const candidates = [
|
|
557
|
-
{ x, y },
|
|
558
|
-
{ x: safeFabRect.left - width - FAB_SAFE_GAP, y },
|
|
559
|
-
{ x: safeFabRect.right + FAB_SAFE_GAP, y },
|
|
560
|
-
{ x, y: safeFabRect.top - height - FAB_SAFE_GAP },
|
|
561
|
-
{ x, y: safeFabRect.bottom + FAB_SAFE_GAP }
|
|
562
|
-
];
|
|
563
|
-
let best = validatePosition(candidates[0].x, candidates[0].y, width, height);
|
|
564
|
-
let bestOverlap = getOverlapArea(
|
|
565
|
-
{ left: best.x, right: best.x + width, top: best.y, bottom: best.y + height },
|
|
566
|
-
safeFabRect
|
|
567
|
-
);
|
|
568
|
-
for (let i = 1; i < candidates.length; i++) {
|
|
569
|
-
const validated = validatePosition(candidates[i].x, candidates[i].y, width, height);
|
|
570
|
-
const overlap = getOverlapArea(
|
|
571
|
-
{ left: validated.x, right: validated.x + width, top: validated.y, bottom: validated.y + height },
|
|
572
|
-
safeFabRect
|
|
573
|
-
);
|
|
574
|
-
if (overlap < bestOverlap) {
|
|
575
|
-
best = validated;
|
|
576
|
-
bestOverlap = overlap;
|
|
577
|
-
if (overlap === 0) break;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
return best;
|
|
308
|
+
const handleStop = () => {
|
|
309
|
+
chat.stop();
|
|
581
310
|
};
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
let x = 0;
|
|
592
|
-
let y = 0;
|
|
593
|
-
const leftX = fabRect.left - dialogWidth - margin;
|
|
594
|
-
if (leftX >= minMargin) {
|
|
595
|
-
x = leftX;
|
|
596
|
-
y = fabRect.top;
|
|
597
|
-
if (y + dialogHeight > viewportHeight - minMargin) {
|
|
598
|
-
y = viewportHeight - dialogHeight - minMargin;
|
|
599
|
-
}
|
|
600
|
-
if (y < minMargin) {
|
|
601
|
-
y = minMargin;
|
|
602
|
-
}
|
|
603
|
-
} else {
|
|
604
|
-
const rightX = fabRect.right + margin;
|
|
605
|
-
if (rightX + dialogWidth <= viewportWidth - minMargin) {
|
|
606
|
-
x = rightX;
|
|
607
|
-
y = fabRect.top;
|
|
608
|
-
if (y + dialogHeight > viewportHeight - minMargin) {
|
|
609
|
-
y = viewportHeight - dialogHeight - minMargin;
|
|
610
|
-
}
|
|
611
|
-
if (y < minMargin) {
|
|
612
|
-
y = minMargin;
|
|
613
|
-
}
|
|
614
|
-
} else {
|
|
615
|
-
x = (viewportWidth - dialogWidth) / 2;
|
|
616
|
-
const aboveY = fabRect.top - dialogHeight - margin;
|
|
617
|
-
if (aboveY >= minMargin) {
|
|
618
|
-
y = aboveY;
|
|
619
|
-
} else {
|
|
620
|
-
const belowY = fabRect.bottom + margin;
|
|
621
|
-
if (belowY + dialogHeight <= viewportHeight - minMargin) {
|
|
622
|
-
y = belowY;
|
|
623
|
-
} else {
|
|
624
|
-
y = (viewportHeight - dialogHeight) / 2;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
311
|
+
const handleRegenerate = (assistantMessageId) => {
|
|
312
|
+
const msgs = chat.messages;
|
|
313
|
+
const index = msgs.findIndex((m) => m.id === assistantMessageId);
|
|
314
|
+
if (index <= 0) return;
|
|
315
|
+
let userMsg = null;
|
|
316
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
317
|
+
if (msgs[i].role === "user") {
|
|
318
|
+
userMsg = msgs[i];
|
|
319
|
+
break;
|
|
627
320
|
}
|
|
628
321
|
}
|
|
629
|
-
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
position.y = separated.y;
|
|
633
|
-
};
|
|
634
|
-
const toggleCollapse = async () => {
|
|
635
|
-
isCollapsed.value = !isCollapsed.value;
|
|
322
|
+
if (!userMsg) return;
|
|
323
|
+
const userText = userMsg.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
324
|
+
chat.messages = msgs.slice(0, msgs.indexOf(userMsg));
|
|
636
325
|
nextTick(() => {
|
|
637
|
-
|
|
638
|
-
const validated = validatePosition(position.x, position.y, containerWidth.value, currentHeight);
|
|
639
|
-
const separated = keepDistanceFromFab(validated.x, validated.y, containerWidth.value, currentHeight);
|
|
640
|
-
position.x = separated.x;
|
|
641
|
-
position.y = separated.y;
|
|
326
|
+
chat.sendMessage({ text: userText });
|
|
642
327
|
});
|
|
643
328
|
};
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
drag.offsetX = position.x;
|
|
656
|
-
drag.offsetY = position.y;
|
|
657
|
-
document.addEventListener("mousemove", onDrag);
|
|
658
|
-
document.addEventListener("mouseup", stopDrag);
|
|
329
|
+
const handleCopy = async (message) => {
|
|
330
|
+
const text = message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
331
|
+
try {
|
|
332
|
+
await navigator.clipboard.writeText(text);
|
|
333
|
+
copiedId.value = message.id;
|
|
334
|
+
setTimeout(() => {
|
|
335
|
+
copiedId.value = null;
|
|
336
|
+
}, 2e3);
|
|
337
|
+
} catch {
|
|
338
|
+
console.error("Failed to copy");
|
|
339
|
+
}
|
|
659
340
|
};
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
const newX = drag.offsetX + (e.clientX - drag.startX);
|
|
663
|
-
const newY = drag.offsetY + (e.clientY - drag.startY);
|
|
664
|
-
const validated = validatePosition(newX, newY, containerWidth.value, isCollapsed.value ? 60 : containerHeight.value);
|
|
665
|
-
position.x = validated.x;
|
|
666
|
-
position.y = validated.y;
|
|
341
|
+
const isToolPart = (part) => {
|
|
342
|
+
return typeof part.type === "string" && part.type.startsWith("tool-");
|
|
667
343
|
};
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
document.removeEventListener("mousemove", onDrag);
|
|
671
|
-
document.removeEventListener("mouseup", stopDrag);
|
|
344
|
+
const isToolLoading = (part) => {
|
|
345
|
+
return part.state === "partial-call" || part.state === "call" || part.state === "input-streaming" || part.state === "input-available";
|
|
672
346
|
};
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
visible.value = true;
|
|
676
|
-
positionReady.value = false;
|
|
677
|
-
emit("wakeUp", true);
|
|
678
|
-
await nextTick();
|
|
679
|
-
if (isFirstOpen.value) {
|
|
680
|
-
calculateInitialPosition();
|
|
681
|
-
isFirstOpen.value = false;
|
|
682
|
-
} else {
|
|
683
|
-
const validated = validatePosition(
|
|
684
|
-
position.x,
|
|
685
|
-
position.y,
|
|
686
|
-
containerWidth.value,
|
|
687
|
-
isCollapsed.value ? 60 : containerHeight.value
|
|
688
|
-
);
|
|
689
|
-
const separated = keepDistanceFromFab(
|
|
690
|
-
validated.x,
|
|
691
|
-
validated.y,
|
|
692
|
-
containerWidth.value,
|
|
693
|
-
isCollapsed.value ? 60 : containerHeight.value
|
|
694
|
-
);
|
|
695
|
-
position.x = separated.x;
|
|
696
|
-
position.y = separated.y;
|
|
697
|
-
}
|
|
698
|
-
await nextTick();
|
|
699
|
-
positionReady.value = true;
|
|
700
|
-
} else {
|
|
701
|
-
positionReady.value = false;
|
|
702
|
-
visible.value = false;
|
|
703
|
-
isCollapsed.value = false;
|
|
704
|
-
emit("wakeUp", false);
|
|
705
|
-
}
|
|
347
|
+
const isToolDone = (part) => {
|
|
348
|
+
return part.state === "result" || part.state === "output-available";
|
|
706
349
|
};
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
aiChatbotX.setTheme(currentTheme.value);
|
|
350
|
+
const isToolError = (part) => {
|
|
351
|
+
return part.state === "error" || part.state === "output-error";
|
|
710
352
|
};
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
});
|
|
740
|
-
return (_ctx, _cache) => {
|
|
741
|
-
return openBlock(), createElementBlock("div", {
|
|
742
|
-
class: "
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
createElementVNode("
|
|
748
|
-
ref_key: "fabRef",
|
|
749
|
-
ref: fabRef,
|
|
750
|
-
class: "assistant-fab",
|
|
751
|
-
onClick: _cache[0] || (_cache[0] = ($event) => toggleDialog(!visible.value))
|
|
752
|
-
}, [
|
|
753
|
-
!isProcessing.value ? (openBlock(), createBlock(VoiceStatus, {
|
|
754
|
-
key: 0,
|
|
755
|
-
class: "voice-status",
|
|
756
|
-
status: voiceStatus.value,
|
|
757
|
-
"transcription-text": transcriptionText.value,
|
|
758
|
-
"is-transcribing": isTranscribing.value,
|
|
759
|
-
style: { "width": "480px" }
|
|
760
|
-
}, null, 8, ["status", "transcription-text", "is-transcribing"])) : (openBlock(), createBlock(ExecutionStatus, {
|
|
761
|
-
key: 1,
|
|
762
|
-
class: "voice-status",
|
|
763
|
-
visible: isProcessing.value,
|
|
764
|
-
text: transcriptionText.value
|
|
765
|
-
}, null, 8, ["visible", "text"])),
|
|
766
|
-
createElementVNode("div", _hoisted_2$1, [
|
|
767
|
-
createElementVNode("img", {
|
|
768
|
-
src: __props.xLogo ? __props.xLogo : "/sime.png",
|
|
769
|
-
alt: "assistant",
|
|
770
|
-
style: normalizeStyle({
|
|
771
|
-
width: __props.xSize?.width + "px"
|
|
772
|
-
})
|
|
773
|
-
}, null, 12, _hoisted_3$1),
|
|
774
|
-
createVNode(Transition, { name: "indicator-fade" }, {
|
|
775
|
-
default: withCtx(() => [
|
|
776
|
-
voiceStatus.value === "listening" ? (openBlock(), createElementBlock("div", {
|
|
777
|
-
key: 0,
|
|
778
|
-
class: normalizeClass(["listening-badge", { "wake-active": wakeAnimating.value }])
|
|
779
|
-
}, [..._cache[3] || (_cache[3] = [
|
|
780
|
-
createElementVNode("div", { class: "listening-waves" }, [
|
|
781
|
-
createElementVNode("div", { class: "wave wave-1" }),
|
|
782
|
-
createElementVNode("div", { class: "wave wave-2" }),
|
|
783
|
-
createElementVNode("div", { class: "wave wave-3" })
|
|
784
|
-
], -1),
|
|
785
|
-
createElementVNode("div", { class: "listening-icon" }, [
|
|
786
|
-
createElementVNode("svg", {
|
|
787
|
-
width: "24",
|
|
788
|
-
height: "24",
|
|
789
|
-
viewBox: "0 0 24 24",
|
|
790
|
-
fill: "none"
|
|
791
|
-
}, [
|
|
792
|
-
createElementVNode("path", {
|
|
793
|
-
d: "M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z",
|
|
794
|
-
fill: "currentColor"
|
|
795
|
-
}),
|
|
796
|
-
createElementVNode("path", {
|
|
797
|
-
d: "M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5",
|
|
798
|
-
stroke: "currentColor",
|
|
799
|
-
"stroke-width": "2",
|
|
800
|
-
"stroke-linecap": "round"
|
|
801
|
-
})
|
|
802
|
-
])
|
|
803
|
-
], -1)
|
|
804
|
-
])], 2)) : createCommentVNode("", true)
|
|
805
|
-
]),
|
|
806
|
-
_: 1
|
|
807
|
-
})
|
|
808
|
-
]),
|
|
809
|
-
createElementVNode("div", {
|
|
810
|
-
class: normalizeClass(["fab-pulse", { active: voiceStatus.value === "listening" }])
|
|
811
|
-
}, null, 2)
|
|
812
|
-
], 512)
|
|
353
|
+
const getToolName = (part) => {
|
|
354
|
+
return part.toolName || part.type?.replace("tool-", "") || "unknown";
|
|
355
|
+
};
|
|
356
|
+
const getToolDisplayName = (name) => {
|
|
357
|
+
return props.toolNames?.[name] || toolDisplayNames[name] || name;
|
|
358
|
+
};
|
|
359
|
+
const isStreamingMessage = (messageId) => {
|
|
360
|
+
if (chat.status !== "streaming" && chat.status !== "submitted") return false;
|
|
361
|
+
const msgs = chat.messages;
|
|
362
|
+
return msgs.length > 0 && msgs[msgs.length - 1].id === messageId;
|
|
363
|
+
};
|
|
364
|
+
const toggleReasoning = (messageId) => {
|
|
365
|
+
reasoningOpen[messageId] = !reasoningOpen[messageId];
|
|
366
|
+
};
|
|
367
|
+
const renderMarkdown = (text) => {
|
|
368
|
+
if (!text) return "";
|
|
369
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>").replace(/\*(.*?)\*/g, "<em>$1</em>").replace(/`([^`]+)`/g, "<code>$1</code>").replace(/```(\w*)\n([\s\S]*?)```/g, '<pre><code class="language-$1">$2</code></pre>').replace(/\n/g, "<br/>");
|
|
370
|
+
};
|
|
371
|
+
onMounted(() => {
|
|
372
|
+
autoResize();
|
|
373
|
+
});
|
|
374
|
+
__expose({
|
|
375
|
+
chat,
|
|
376
|
+
sendMessage: (text) => chat.sendMessage({ text }),
|
|
377
|
+
clearMessages: () => {
|
|
378
|
+
chat.messages = [];
|
|
379
|
+
},
|
|
380
|
+
stop: () => chat.stop()
|
|
381
|
+
});
|
|
382
|
+
return (_ctx, _cache) => {
|
|
383
|
+
return openBlock(), createElementBlock("div", {
|
|
384
|
+
class: normalizeClass(["ai-chat", { "ai-chat--full-width": __props.fullWidth }])
|
|
385
|
+
}, [
|
|
386
|
+
isEmpty.value ? (openBlock(), createElementBlock("div", _hoisted_1$1, [
|
|
387
|
+
createElementVNode("div", _hoisted_2$1, [
|
|
388
|
+
createElementVNode("h1", _hoisted_3$1, toDisplayString(__props.welcomeTitle), 1),
|
|
389
|
+
createElementVNode("p", _hoisted_4$1, toDisplayString(__props.welcomeDescription), 1)
|
|
813
390
|
]),
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
createElementVNode("div", {
|
|
819
|
-
ref: "dialogRef",
|
|
820
|
-
class: normalizeClass(["x-dialog-container", {
|
|
821
|
-
collapsed: isCollapsed.value,
|
|
822
|
-
"is-hidden": !visible.value,
|
|
823
|
-
"position-ready": positionReady.value
|
|
824
|
-
}]),
|
|
825
|
-
style: normalizeStyle({
|
|
826
|
-
width: containerWidth.value + "px",
|
|
827
|
-
height: isCollapsed.value ? "auto" : containerHeight.value + "px",
|
|
828
|
-
border: currentTheme.value === "light" && !isCollapsed.value ? "1px solid var(--border-color)" : "none",
|
|
829
|
-
"--dialog-x": position.x + "px",
|
|
830
|
-
"--dialog-y": position.y + "px"
|
|
831
|
-
}),
|
|
832
|
-
onMousedown: startDrag
|
|
391
|
+
createElementVNode("div", _hoisted_5$1, [
|
|
392
|
+
createElementVNode("form", {
|
|
393
|
+
class: "ai-chat__form",
|
|
394
|
+
onSubmit: withModifiers(handleSubmit, ["prevent"])
|
|
833
395
|
}, [
|
|
834
|
-
createElementVNode("div",
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
]
|
|
846
|
-
createElementVNode("span", _hoisted_7$1, toDisplayString(__props.xTitle), 1)
|
|
396
|
+
createElementVNode("div", _hoisted_6$1, [
|
|
397
|
+
withDirectives(createElementVNode("textarea", {
|
|
398
|
+
ref_key: "textareaRef",
|
|
399
|
+
ref: textareaRef,
|
|
400
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => inputText.value = $event),
|
|
401
|
+
class: "ai-chat__textarea",
|
|
402
|
+
placeholder: "发送消息...",
|
|
403
|
+
rows: "1",
|
|
404
|
+
onKeydown: handleKeydown,
|
|
405
|
+
onInput: autoResize
|
|
406
|
+
}, null, 544), [
|
|
407
|
+
[vModelText, inputText.value]
|
|
847
408
|
]),
|
|
848
|
-
createElementVNode("
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
stroke: "currentColor",
|
|
863
|
-
"stroke-width": "2",
|
|
864
|
-
"stroke-linecap": "round"
|
|
865
|
-
})
|
|
866
|
-
], -1)
|
|
867
|
-
])]),
|
|
868
|
-
createElementVNode("button", {
|
|
869
|
-
class: normalizeClass(["action-btn theme-btn", {
|
|
870
|
-
active: voiceStatus.value !== "standby",
|
|
871
|
-
listening: voiceStatus.value === "listening",
|
|
872
|
-
woke: voiceStatus.value === "wake"
|
|
873
|
-
}]),
|
|
874
|
-
onClick: _cache[1] || (_cache[1] = withModifiers(() => toggleVoiceMode(), ["stop"])),
|
|
875
|
-
title: voiceButtonTooltip.value
|
|
409
|
+
createElementVNode("button", {
|
|
410
|
+
type: "submit",
|
|
411
|
+
class: "ai-chat__send-btn",
|
|
412
|
+
disabled: !inputText.value.trim() || isLoading.value
|
|
413
|
+
}, [..._cache[2] || (_cache[2] = [
|
|
414
|
+
createElementVNode("svg", {
|
|
415
|
+
width: "16",
|
|
416
|
+
height: "16",
|
|
417
|
+
viewBox: "0 0 24 24",
|
|
418
|
+
fill: "none",
|
|
419
|
+
stroke: "currentColor",
|
|
420
|
+
"stroke-width": "2",
|
|
421
|
+
"stroke-linecap": "round",
|
|
422
|
+
"stroke-linejoin": "round"
|
|
876
423
|
}, [
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
424
|
+
createElementVNode("line", {
|
|
425
|
+
x1: "22",
|
|
426
|
+
y1: "2",
|
|
427
|
+
x2: "11",
|
|
428
|
+
y2: "13"
|
|
429
|
+
}),
|
|
430
|
+
createElementVNode("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
431
|
+
], -1)
|
|
432
|
+
])], 8, _hoisted_7$1)
|
|
433
|
+
])
|
|
434
|
+
], 32),
|
|
435
|
+
__props.suggestions.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_8$1, [
|
|
436
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(__props.suggestions, (suggestion) => {
|
|
437
|
+
return openBlock(), createElementBlock("button", {
|
|
438
|
+
key: suggestion,
|
|
439
|
+
class: "ai-chat__suggestion",
|
|
440
|
+
onClick: ($event) => handleSuggestionClick(suggestion)
|
|
441
|
+
}, toDisplayString(suggestion), 9, _hoisted_9$1);
|
|
442
|
+
}), 128))
|
|
443
|
+
])) : createCommentVNode("", true)
|
|
444
|
+
])
|
|
445
|
+
])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
446
|
+
createElementVNode("div", {
|
|
447
|
+
ref_key: "messagesContainerRef",
|
|
448
|
+
ref: messagesContainerRef,
|
|
449
|
+
class: "ai-chat__messages",
|
|
450
|
+
onScroll: handleScroll
|
|
451
|
+
}, [
|
|
452
|
+
createElementVNode("div", _hoisted_10$1, [
|
|
453
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(chat).messages, (message) => {
|
|
454
|
+
return openBlock(), createElementBlock("div", {
|
|
455
|
+
key: message.id,
|
|
456
|
+
class: "ai-chat__message-group"
|
|
457
|
+
}, [
|
|
458
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(message.parts, (part, partIndex) => {
|
|
459
|
+
return openBlock(), createElementBlock(Fragment, {
|
|
460
|
+
key: `${message.id}-${partIndex}`
|
|
882
461
|
}, [
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
"
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
"
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
462
|
+
part.type === "text" ? (openBlock(), createElementBlock("div", {
|
|
463
|
+
key: 0,
|
|
464
|
+
class: normalizeClass(["ai-chat__message", `ai-chat__message--${message.role}`])
|
|
465
|
+
}, [
|
|
466
|
+
createElementVNode("div", _hoisted_11$1, [
|
|
467
|
+
createElementVNode("div", {
|
|
468
|
+
class: "ai-chat__message-text",
|
|
469
|
+
innerHTML: renderMarkdown(part.text)
|
|
470
|
+
}, null, 8, _hoisted_12$1)
|
|
471
|
+
])
|
|
472
|
+
], 2)) : part.type === "reasoning" ? (openBlock(), createElementBlock("div", _hoisted_13$1, [
|
|
473
|
+
createElementVNode("button", {
|
|
474
|
+
class: "ai-chat__reasoning-trigger",
|
|
475
|
+
onClick: ($event) => toggleReasoning(message.id)
|
|
476
|
+
}, [
|
|
477
|
+
(openBlock(), createElementBlock("svg", {
|
|
478
|
+
class: normalizeClass(["ai-chat__reasoning-icon", { "ai-chat__reasoning-icon--open": reasoningOpen[message.id] }]),
|
|
479
|
+
width: "12",
|
|
480
|
+
height: "12",
|
|
481
|
+
viewBox: "0 0 24 24",
|
|
482
|
+
fill: "none",
|
|
483
|
+
stroke: "currentColor",
|
|
484
|
+
"stroke-width": "2"
|
|
485
|
+
}, [..._cache[3] || (_cache[3] = [
|
|
486
|
+
createElementVNode("polyline", { points: "9 18 15 12 9 6" }, null, -1)
|
|
487
|
+
])], 2)),
|
|
488
|
+
_cache[4] || (_cache[4] = createElementVNode("span", null, "思考过程", -1)),
|
|
489
|
+
isStreamingMessage(message.id) && partIndex === message.parts.length - 1 ? (openBlock(), createElementBlock("span", _hoisted_15)) : createCommentVNode("", true)
|
|
490
|
+
], 8, _hoisted_14),
|
|
491
|
+
reasoningOpen[message.id] ? (openBlock(), createElementBlock("div", _hoisted_16, toDisplayString(part.text), 1)) : createCommentVNode("", true)
|
|
492
|
+
])) : isToolPart(part) ? (openBlock(), createElementBlock("div", _hoisted_17, [
|
|
493
|
+
createElementVNode("div", {
|
|
494
|
+
class: normalizeClass(["ai-chat__tool-step", {
|
|
495
|
+
"ai-chat__tool-step--loading": isToolLoading(part),
|
|
496
|
+
"ai-chat__tool-step--done": isToolDone(part),
|
|
497
|
+
"ai-chat__tool-step--error": isToolError(part)
|
|
498
|
+
}])
|
|
499
|
+
}, [
|
|
500
|
+
createElementVNode("span", _hoisted_18, [
|
|
501
|
+
isToolLoading(part) ? (openBlock(), createElementBlock("svg", _hoisted_19, [..._cache[5] || (_cache[5] = [
|
|
502
|
+
createElementVNode("circle", {
|
|
503
|
+
cx: "12",
|
|
504
|
+
cy: "12",
|
|
505
|
+
r: "10",
|
|
506
|
+
stroke: "currentColor",
|
|
507
|
+
"stroke-width": "2.5",
|
|
508
|
+
"stroke-linecap": "round",
|
|
509
|
+
"stroke-dasharray": "31.4 31.4"
|
|
510
|
+
}, null, -1)
|
|
511
|
+
])])) : isToolDone(part) ? (openBlock(), createElementBlock("svg", _hoisted_20, [..._cache[6] || (_cache[6] = [
|
|
512
|
+
createElementVNode("path", {
|
|
513
|
+
d: "M20 6L9 17l-5-5",
|
|
514
|
+
stroke: "currentColor",
|
|
515
|
+
"stroke-width": "2.5",
|
|
516
|
+
"stroke-linecap": "round",
|
|
517
|
+
"stroke-linejoin": "round"
|
|
518
|
+
}, null, -1)
|
|
519
|
+
])])) : isToolError(part) ? (openBlock(), createElementBlock("svg", _hoisted_21, [..._cache[7] || (_cache[7] = [
|
|
520
|
+
createElementVNode("circle", {
|
|
521
|
+
cx: "12",
|
|
522
|
+
cy: "12",
|
|
523
|
+
r: "10",
|
|
524
|
+
stroke: "currentColor",
|
|
525
|
+
"stroke-width": "2"
|
|
526
|
+
}, null, -1),
|
|
527
|
+
createElementVNode("path", {
|
|
528
|
+
d: "M15 9l-6 6M9 9l6 6",
|
|
529
|
+
stroke: "currentColor",
|
|
530
|
+
"stroke-width": "2",
|
|
531
|
+
"stroke-linecap": "round"
|
|
532
|
+
}, null, -1)
|
|
533
|
+
])])) : createCommentVNode("", true)
|
|
534
|
+
]),
|
|
535
|
+
createElementVNode("span", _hoisted_22, toDisplayString(getToolDisplayName(getToolName(part))), 1)
|
|
536
|
+
], 2)
|
|
537
|
+
])) : createCommentVNode("", true)
|
|
538
|
+
], 64);
|
|
539
|
+
}), 128)),
|
|
540
|
+
message.role === "assistant" && !isStreamingMessage(message.id) ? (openBlock(), createElementBlock("div", _hoisted_23, [
|
|
541
|
+
createElementVNode("button", {
|
|
542
|
+
class: "ai-chat__action-btn",
|
|
543
|
+
title: "重新生成",
|
|
544
|
+
onClick: ($event) => handleRegenerate(message.id)
|
|
545
|
+
}, [..._cache[8] || (_cache[8] = [
|
|
546
|
+
createElementVNode("svg", {
|
|
547
|
+
width: "12",
|
|
548
|
+
height: "12",
|
|
549
|
+
viewBox: "0 0 24 24",
|
|
550
|
+
fill: "none",
|
|
906
551
|
stroke: "currentColor",
|
|
907
552
|
"stroke-width": "2",
|
|
908
553
|
"stroke-linecap": "round",
|
|
909
554
|
"stroke-linejoin": "round"
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
width: "16",
|
|
921
|
-
height: "16",
|
|
922
|
-
viewBox: "0 0 24 24",
|
|
923
|
-
fill: "none"
|
|
555
|
+
}, [
|
|
556
|
+
createElementVNode("polyline", { points: "23 4 23 10 17 10" }),
|
|
557
|
+
createElementVNode("polyline", { points: "1 20 1 14 7 14" }),
|
|
558
|
+
createElementVNode("path", { d: "M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" })
|
|
559
|
+
], -1)
|
|
560
|
+
])], 8, _hoisted_24),
|
|
561
|
+
createElementVNode("button", {
|
|
562
|
+
class: "ai-chat__action-btn",
|
|
563
|
+
title: "复制",
|
|
564
|
+
onClick: ($event) => handleCopy(message)
|
|
924
565
|
}, [
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
566
|
+
!copiedId.value || copiedId.value !== message.id ? (openBlock(), createElementBlock("svg", _hoisted_26, [..._cache[9] || (_cache[9] = [
|
|
567
|
+
createElementVNode("rect", {
|
|
568
|
+
x: "9",
|
|
569
|
+
y: "9",
|
|
570
|
+
width: "13",
|
|
571
|
+
height: "13",
|
|
572
|
+
rx: "2",
|
|
573
|
+
ry: "2"
|
|
574
|
+
}, null, -1),
|
|
575
|
+
createElementVNode("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" }, null, -1)
|
|
576
|
+
])])) : (openBlock(), createElementBlock("svg", _hoisted_27, [..._cache[10] || (_cache[10] = [
|
|
577
|
+
createElementVNode("polyline", { points: "20 6 9 17 4 12" }, null, -1)
|
|
578
|
+
])]))
|
|
579
|
+
], 8, _hoisted_25)
|
|
580
|
+
])) : createCommentVNode("", true)
|
|
581
|
+
]);
|
|
582
|
+
}), 128)),
|
|
583
|
+
unref(chat).status === "submitted" ? (openBlock(), createElementBlock("div", _hoisted_28, [..._cache[11] || (_cache[11] = [
|
|
584
|
+
createElementVNode("div", { class: "ai-chat__thinking-dots" }, [
|
|
585
|
+
createElementVNode("span"),
|
|
586
|
+
createElementVNode("span"),
|
|
587
|
+
createElementVNode("span")
|
|
588
|
+
], -1)
|
|
589
|
+
])])) : createCommentVNode("", true)
|
|
590
|
+
])
|
|
591
|
+
], 544),
|
|
592
|
+
createVNode(Transition, { name: "fade" }, {
|
|
593
|
+
default: withCtx(() => [
|
|
594
|
+
showScrollButton.value ? (openBlock(), createElementBlock("button", {
|
|
595
|
+
key: 0,
|
|
596
|
+
class: "ai-chat__scroll-btn",
|
|
597
|
+
onClick: scrollToBottom
|
|
598
|
+
}, [..._cache[12] || (_cache[12] = [
|
|
599
|
+
createElementVNode("svg", {
|
|
600
|
+
width: "16",
|
|
601
|
+
height: "16",
|
|
602
|
+
viewBox: "0 0 24 24",
|
|
603
|
+
fill: "none",
|
|
604
|
+
stroke: "currentColor",
|
|
605
|
+
"stroke-width": "2",
|
|
606
|
+
"stroke-linecap": "round",
|
|
607
|
+
"stroke-linejoin": "round"
|
|
608
|
+
}, [
|
|
609
|
+
createElementVNode("polyline", { points: "6 9 12 15 18 9" })
|
|
610
|
+
], -1)
|
|
611
|
+
])])) : createCommentVNode("", true)
|
|
612
|
+
]),
|
|
613
|
+
_: 1
|
|
614
|
+
}),
|
|
615
|
+
!__props.isReadonly ? (openBlock(), createElementBlock("div", _hoisted_29, [
|
|
616
|
+
createElementVNode("form", {
|
|
617
|
+
class: "ai-chat__form",
|
|
618
|
+
onSubmit: withModifiers(handleSubmit, ["prevent"])
|
|
619
|
+
}, [
|
|
620
|
+
createElementVNode("div", _hoisted_30, [
|
|
621
|
+
withDirectives(createElementVNode("textarea", {
|
|
622
|
+
ref_key: "textareaRef",
|
|
623
|
+
ref: textareaRef,
|
|
624
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => inputText.value = $event),
|
|
625
|
+
class: "ai-chat__textarea",
|
|
626
|
+
placeholder: "发送消息...",
|
|
627
|
+
rows: "1",
|
|
628
|
+
onKeydown: handleKeydown,
|
|
629
|
+
onInput: autoResize
|
|
630
|
+
}, null, 544), [
|
|
631
|
+
[vModelText, inputText.value]
|
|
632
|
+
]),
|
|
633
|
+
unref(chat).status === "streaming" || unref(chat).status === "submitted" ? (openBlock(), createElementBlock("button", {
|
|
634
|
+
key: 0,
|
|
635
|
+
type: "button",
|
|
636
|
+
class: "ai-chat__stop-btn",
|
|
637
|
+
onClick: handleStop
|
|
638
|
+
}, [..._cache[13] || (_cache[13] = [
|
|
639
|
+
createElementVNode("svg", {
|
|
640
|
+
width: "14",
|
|
641
|
+
height: "14",
|
|
642
|
+
viewBox: "0 0 24 24",
|
|
643
|
+
fill: "currentColor"
|
|
942
644
|
}, [
|
|
943
|
-
(
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
class: "x-iframe",
|
|
982
|
-
allow: "microphone *; storage-access *; camera *",
|
|
983
|
-
frameborder: "0",
|
|
984
|
-
onLoad: handleIframeLoad
|
|
985
|
-
}, null, 40, _hoisted_15)
|
|
986
|
-
], 6)
|
|
987
|
-
], 38)
|
|
988
|
-
]),
|
|
989
|
-
_: 1
|
|
990
|
-
})
|
|
991
|
-
], 8, _hoisted_1$1);
|
|
645
|
+
createElementVNode("rect", {
|
|
646
|
+
x: "6",
|
|
647
|
+
y: "6",
|
|
648
|
+
width: "12",
|
|
649
|
+
height: "12",
|
|
650
|
+
rx: "2"
|
|
651
|
+
})
|
|
652
|
+
], -1)
|
|
653
|
+
])])) : (openBlock(), createElementBlock("button", {
|
|
654
|
+
key: 1,
|
|
655
|
+
type: "submit",
|
|
656
|
+
class: "ai-chat__send-btn",
|
|
657
|
+
disabled: !inputText.value.trim()
|
|
658
|
+
}, [..._cache[14] || (_cache[14] = [
|
|
659
|
+
createElementVNode("svg", {
|
|
660
|
+
width: "16",
|
|
661
|
+
height: "16",
|
|
662
|
+
viewBox: "0 0 24 24",
|
|
663
|
+
fill: "none",
|
|
664
|
+
stroke: "currentColor",
|
|
665
|
+
"stroke-width": "2",
|
|
666
|
+
"stroke-linecap": "round",
|
|
667
|
+
"stroke-linejoin": "round"
|
|
668
|
+
}, [
|
|
669
|
+
createElementVNode("line", {
|
|
670
|
+
x1: "22",
|
|
671
|
+
y1: "2",
|
|
672
|
+
x2: "11",
|
|
673
|
+
y2: "13"
|
|
674
|
+
}),
|
|
675
|
+
createElementVNode("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
676
|
+
], -1)
|
|
677
|
+
])], 8, _hoisted_31))
|
|
678
|
+
])
|
|
679
|
+
], 32)
|
|
680
|
+
])) : createCommentVNode("", true)
|
|
681
|
+
], 64))
|
|
682
|
+
], 2);
|
|
992
683
|
};
|
|
993
684
|
}
|
|
994
685
|
});
|
|
995
686
|
|
|
996
|
-
const
|
|
687
|
+
const _export_sfc = (sfc, props) => {
|
|
688
|
+
const target = sfc.__vccOpts || sfc;
|
|
689
|
+
for (const [key, val] of props) {
|
|
690
|
+
target[key] = val;
|
|
691
|
+
}
|
|
692
|
+
return target;
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
const aiChat = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-958fd919"]]);
|
|
696
|
+
|
|
697
|
+
class CommandManager {
|
|
698
|
+
commands = /* @__PURE__ */ new Map();
|
|
699
|
+
debug;
|
|
700
|
+
constructor(options = {}) {
|
|
701
|
+
this.debug = options.debug ?? false;
|
|
702
|
+
}
|
|
703
|
+
registerCommand(command) {
|
|
704
|
+
this.commands.set(command.name, command);
|
|
705
|
+
this.log("注册命令", `${command.name}: ${command.description}`);
|
|
706
|
+
}
|
|
707
|
+
unregisterCommand(name) {
|
|
708
|
+
const deleted = this.commands.delete(name);
|
|
709
|
+
if (deleted) {
|
|
710
|
+
this.log("命令已注销", name);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
async executeCommand(command, args = []) {
|
|
714
|
+
const commandDef = this.commands.get(command);
|
|
715
|
+
if (!commandDef) {
|
|
716
|
+
throw new Error(`命令 "${command}" 未找到`);
|
|
717
|
+
}
|
|
718
|
+
this.log("执行命令", command, args);
|
|
719
|
+
return await commandDef.handler(...args);
|
|
720
|
+
}
|
|
721
|
+
getCommands() {
|
|
722
|
+
return Array.from(this.commands.values()).map((cmd) => ({
|
|
723
|
+
name: cmd.name,
|
|
724
|
+
description: cmd.description,
|
|
725
|
+
parameters: cmd.parameters
|
|
726
|
+
}));
|
|
727
|
+
}
|
|
728
|
+
hasCommand(name) {
|
|
729
|
+
return this.commands.has(name);
|
|
730
|
+
}
|
|
731
|
+
clear() {
|
|
732
|
+
this.commands.clear();
|
|
733
|
+
this.log("", "所有命令已清空");
|
|
734
|
+
}
|
|
735
|
+
log(prefix, msg, ...args) {
|
|
736
|
+
(/* @__PURE__ */ new Date()).toLocaleTimeString([], {
|
|
737
|
+
hour: "2-digit",
|
|
738
|
+
minute: "2-digit",
|
|
739
|
+
second: "2-digit"
|
|
740
|
+
});
|
|
741
|
+
console.log(
|
|
742
|
+
`%c ${prefix}`,
|
|
743
|
+
"background:#7c3aed;color:white;padding:2px 6px;border-radius:3px 0 0 3px;font-weight:bold;",
|
|
744
|
+
`${msg}`
|
|
745
|
+
);
|
|
746
|
+
if (args.length > 0) {
|
|
747
|
+
console.log(...args);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const AiChatbotXKey = Symbol("sime-x");
|
|
753
|
+
function injectStrict(key, defaultValue, treatDefaultAsFactory) {
|
|
754
|
+
let result;
|
|
755
|
+
if (defaultValue === void 0) {
|
|
756
|
+
result = inject(key);
|
|
757
|
+
} else if (treatDefaultAsFactory === true) {
|
|
758
|
+
result = inject(key, defaultValue, true);
|
|
759
|
+
} else {
|
|
760
|
+
result = inject(key, defaultValue, false);
|
|
761
|
+
}
|
|
762
|
+
if (!result) {
|
|
763
|
+
throw new Error(`Could not resolve ${key.description}`);
|
|
764
|
+
}
|
|
765
|
+
return result;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
769
|
+
__name: "sime-provider",
|
|
770
|
+
props: {
|
|
771
|
+
project: {},
|
|
772
|
+
description: {},
|
|
773
|
+
debug: { type: Boolean },
|
|
774
|
+
chatbotUrl: {},
|
|
775
|
+
appId: {},
|
|
776
|
+
appToken: {}
|
|
777
|
+
},
|
|
778
|
+
setup(__props) {
|
|
779
|
+
const props = __props;
|
|
780
|
+
const commandManager = shallowRef(new CommandManager({ debug: props.debug ?? false }));
|
|
781
|
+
const stopBroadcastRef = shallowRef(async () => {
|
|
782
|
+
});
|
|
783
|
+
provide(AiChatbotXKey, {
|
|
784
|
+
chatbotUrl: () => props.chatbotUrl,
|
|
785
|
+
appId: () => props.appId,
|
|
786
|
+
appToken: () => props.appToken,
|
|
787
|
+
stopBroadcast: () => stopBroadcastRef.value(),
|
|
788
|
+
registerVoiceMethods: (methods) => {
|
|
789
|
+
if (methods.stopBroadcast) stopBroadcastRef.value = methods.stopBroadcast;
|
|
790
|
+
},
|
|
791
|
+
getCommads: async () => commandManager.value.getCommands(),
|
|
792
|
+
registerCommand: (cmd) => {
|
|
793
|
+
commandManager.value.registerCommand(cmd);
|
|
794
|
+
},
|
|
795
|
+
unregisterCommand: (name) => {
|
|
796
|
+
commandManager.value.unregisterCommand(name);
|
|
797
|
+
},
|
|
798
|
+
async executeCommand(commandName, args = []) {
|
|
799
|
+
return await commandManager.value.executeCommand(commandName, args);
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
return (_ctx, _cache) => {
|
|
803
|
+
return renderSlot(_ctx.$slots, "default");
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
});
|
|
997
807
|
|
|
998
808
|
function useTTS(getVoiceConfig) {
|
|
999
809
|
const isSpeaking = ref(false);
|
|
@@ -1033,9 +843,9 @@ function useTTS(getVoiceConfig) {
|
|
|
1033
843
|
apiSecret: vc.apiSecret,
|
|
1034
844
|
websocketUrl: vc.ttsWebsocketUrl || "wss://tts-api.xfyun.cn/v2/tts",
|
|
1035
845
|
vcn: vc.ttsVcn || "xiaoyan",
|
|
1036
|
-
speed:
|
|
1037
|
-
volume:
|
|
1038
|
-
pitch: 50,
|
|
846
|
+
speed: vc.speed || 55,
|
|
847
|
+
volume: vc.volume || 90,
|
|
848
|
+
pitch: vc.pitch || 50,
|
|
1039
849
|
aue: "raw",
|
|
1040
850
|
auf: "audio/L16;rate=16000",
|
|
1041
851
|
tte: "UTF8",
|
|
@@ -1232,6 +1042,73 @@ function useBubble(options = {}) {
|
|
|
1232
1042
|
};
|
|
1233
1043
|
}
|
|
1234
1044
|
|
|
1045
|
+
const ensureMicrophonePermission = async () => {
|
|
1046
|
+
if (typeof navigator === "undefined" || typeof window === "undefined") {
|
|
1047
|
+
console.log("当前环境不支持麦克风访问");
|
|
1048
|
+
return false;
|
|
1049
|
+
}
|
|
1050
|
+
if (!navigator.mediaDevices?.getUserMedia || !navigator.mediaDevices?.enumerateDevices) {
|
|
1051
|
+
console.log("当前环境不支持麦克风访问");
|
|
1052
|
+
return false;
|
|
1053
|
+
}
|
|
1054
|
+
try {
|
|
1055
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
1056
|
+
const audioInputDevices = devices.filter((device) => device.kind === "audioinput");
|
|
1057
|
+
if (audioInputDevices.length === 0) {
|
|
1058
|
+
console.log("未检测到麦克风设备,请连接麦克风后重试。");
|
|
1059
|
+
return false;
|
|
1060
|
+
}
|
|
1061
|
+
if ("permissions" in navigator && navigator.permissions?.query) {
|
|
1062
|
+
try {
|
|
1063
|
+
const status = await navigator.permissions.query({ name: "microphone" });
|
|
1064
|
+
if (status.state === "denied") {
|
|
1065
|
+
console.log("麦克风权限被禁用,请在浏览器设置中开启。");
|
|
1066
|
+
return false;
|
|
1067
|
+
}
|
|
1068
|
+
} catch (e) {
|
|
1069
|
+
console.warn("Permission query not supported:", e);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
let stream = null;
|
|
1073
|
+
try {
|
|
1074
|
+
stream = await navigator.mediaDevices.getUserMedia({
|
|
1075
|
+
audio: {
|
|
1076
|
+
echoCancellation: true,
|
|
1077
|
+
noiseSuppression: true,
|
|
1078
|
+
autoGainControl: true
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1081
|
+
const audioTracks = stream.getAudioTracks();
|
|
1082
|
+
if (audioTracks.length === 0) {
|
|
1083
|
+
console.log("无法获取麦克风音频轨道。");
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1086
|
+
const activeTrack = audioTracks[0];
|
|
1087
|
+
if (!activeTrack.enabled || activeTrack.readyState !== "live") {
|
|
1088
|
+
console.log("麦克风设备不可用,请检查设备连接。");
|
|
1089
|
+
return false;
|
|
1090
|
+
}
|
|
1091
|
+
return true;
|
|
1092
|
+
} finally {
|
|
1093
|
+
if (stream) {
|
|
1094
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
console.error("Microphone permission check failed", error);
|
|
1099
|
+
if (error.name === "NotFoundError" || error.name === "DevicesNotFoundError") {
|
|
1100
|
+
console.log("未检测到麦克风设备,请连接麦克风后重试。");
|
|
1101
|
+
} else if (error.name === "NotAllowedError" || error.name === "PermissionDeniedError") {
|
|
1102
|
+
console.log("麦克风权限被拒绝,请在浏览器设置中允许访问。");
|
|
1103
|
+
} else if (error.name === "NotReadableError" || error.name === "TrackStartError") {
|
|
1104
|
+
console.log("麦克风被其他应用占用或无法访问。");
|
|
1105
|
+
} else {
|
|
1106
|
+
console.log("无法访问麦克风,请检查设备连接和浏览器权限。");
|
|
1107
|
+
}
|
|
1108
|
+
return false;
|
|
1109
|
+
}
|
|
1110
|
+
};
|
|
1111
|
+
|
|
1235
1112
|
function useVoiceRecognition(options) {
|
|
1236
1113
|
const voiceStatus = ref("standby");
|
|
1237
1114
|
const isTranscribing = ref(false);
|
|
@@ -1457,7 +1334,8 @@ function processUIMessageStreamEvent(payload, callbacks) {
|
|
|
1457
1334
|
callbacks.onFinish?.(parsed);
|
|
1458
1335
|
break;
|
|
1459
1336
|
case "error":
|
|
1460
|
-
|
|
1337
|
+
case "tool-output-error":
|
|
1338
|
+
callbacks.onError?.(parsed.errorText || parsed.error || "Unknown error", parsed);
|
|
1461
1339
|
break;
|
|
1462
1340
|
case "start":
|
|
1463
1341
|
case "text-start":
|
|
@@ -1716,7 +1594,16 @@ async function parseDataStreamToMessage(response, onUpdate) {
|
|
|
1716
1594
|
}
|
|
1717
1595
|
emitUpdate();
|
|
1718
1596
|
},
|
|
1719
|
-
onError(error) {
|
|
1597
|
+
onError(error, data) {
|
|
1598
|
+
const toolCallId = data?.toolCallId;
|
|
1599
|
+
if (toolCallId) {
|
|
1600
|
+
toolCalls.delete(toolCallId);
|
|
1601
|
+
const idx = findToolPartIndex(toolCallId);
|
|
1602
|
+
if (idx !== -1) {
|
|
1603
|
+
parts.splice(idx, 1);
|
|
1604
|
+
emitUpdate();
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1720
1607
|
console.error("[DataStreamParser] stream error:", error);
|
|
1721
1608
|
},
|
|
1722
1609
|
onStepFinish(_data) {
|
|
@@ -1786,20 +1673,28 @@ function useAgentInvoke(options) {
|
|
|
1786
1673
|
currentToolParts.value = [];
|
|
1787
1674
|
executingTools.value = /* @__PURE__ */ new Set();
|
|
1788
1675
|
};
|
|
1676
|
+
const extractExecutableCommands = (payload) => {
|
|
1677
|
+
if (!payload || typeof payload !== "object") return [];
|
|
1678
|
+
const commands = payload.commands;
|
|
1679
|
+
if (!Array.isArray(commands) || commands.length === 0) return [];
|
|
1680
|
+
return commands.filter((cmd) => cmd && typeof cmd === "object" && typeof cmd.name === "string" && cmd.name.trim()).map((cmd) => ({
|
|
1681
|
+
name: cmd.name,
|
|
1682
|
+
args: Array.isArray(cmd.args) ? cmd.args : []
|
|
1683
|
+
}));
|
|
1684
|
+
};
|
|
1789
1685
|
const executeHostCommands = async (toolCallId, result) => {
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
if (!Array.isArray(commands) || commands.length === 0) return;
|
|
1686
|
+
const commands = extractExecutableCommands(result);
|
|
1687
|
+
if (commands.length === 0) return false;
|
|
1793
1688
|
try {
|
|
1794
1689
|
executingTools.value = /* @__PURE__ */ new Set([...executingTools.value, toolCallId]);
|
|
1795
1690
|
for (const cmd of commands) {
|
|
1796
|
-
const args = Array.isArray(cmd.args) ? cmd.args : [];
|
|
1797
1691
|
try {
|
|
1798
|
-
await aiChatbotX.executeCommand(cmd.name, args);
|
|
1692
|
+
await aiChatbotX.executeCommand(cmd.name, cmd.args);
|
|
1799
1693
|
} catch (cmdErr) {
|
|
1800
1694
|
console.error(`[AgentInvoke] 执行命令 ${cmd.name} 失败:`, cmdErr);
|
|
1801
1695
|
}
|
|
1802
1696
|
}
|
|
1697
|
+
return true;
|
|
1803
1698
|
} finally {
|
|
1804
1699
|
const next = new Set(executingTools.value);
|
|
1805
1700
|
next.delete(toolCallId);
|
|
@@ -1833,8 +1728,9 @@ function useAgentInvoke(options) {
|
|
|
1833
1728
|
bubble.open();
|
|
1834
1729
|
let prevTextLength = 0;
|
|
1835
1730
|
const processedToolResults = /* @__PURE__ */ new Set();
|
|
1731
|
+
const processingToolResults = /* @__PURE__ */ new Set();
|
|
1836
1732
|
abortController = new AbortController();
|
|
1837
|
-
const commands = await aiChatbotX.
|
|
1733
|
+
const commands = await aiChatbotX.getCommads();
|
|
1838
1734
|
conversationHistory.value.length > 0 ? [...conversationHistory.value] : void 0;
|
|
1839
1735
|
try {
|
|
1840
1736
|
const response = await fetch(options.endpoint.value, {
|
|
@@ -1870,7 +1766,8 @@ function useAgentInvoke(options) {
|
|
|
1870
1766
|
};
|
|
1871
1767
|
currentToolParts.value = [...currentToolParts.value, toolPart];
|
|
1872
1768
|
if (tr.toolName === "executeCommand") {
|
|
1873
|
-
|
|
1769
|
+
console.log("executeCommand", tr.result);
|
|
1770
|
+
void executeHostCommands(toolPart.toolCallId, tr.result);
|
|
1874
1771
|
}
|
|
1875
1772
|
}
|
|
1876
1773
|
}
|
|
@@ -1887,13 +1784,23 @@ function useAgentInvoke(options) {
|
|
|
1887
1784
|
);
|
|
1888
1785
|
currentToolParts.value = toolParts;
|
|
1889
1786
|
for (const part of toolParts) {
|
|
1890
|
-
if (part.toolName === "executeCommand" && !processedToolResults.has(part.toolCallId)) {
|
|
1787
|
+
if (part.toolName === "executeCommand" && !processedToolResults.has(part.toolCallId) && !processingToolResults.has(part.toolCallId)) {
|
|
1891
1788
|
if (part.type === "tool-call" && part.state === "call" && part.args) {
|
|
1892
|
-
|
|
1893
|
-
executeHostCommands(part.toolCallId, part.args)
|
|
1789
|
+
processingToolResults.add(part.toolCallId);
|
|
1790
|
+
void executeHostCommands(part.toolCallId, part.args).then((executed) => {
|
|
1791
|
+
if (executed) {
|
|
1792
|
+
processedToolResults.add(part.toolCallId);
|
|
1793
|
+
}
|
|
1794
|
+
processingToolResults.delete(part.toolCallId);
|
|
1795
|
+
});
|
|
1894
1796
|
} else if (part.type === "tool-result" && part.result) {
|
|
1895
|
-
|
|
1896
|
-
executeHostCommands(part.toolCallId, part.result)
|
|
1797
|
+
processingToolResults.add(part.toolCallId);
|
|
1798
|
+
void executeHostCommands(part.toolCallId, part.result).then((executed) => {
|
|
1799
|
+
if (executed) {
|
|
1800
|
+
processedToolResults.add(part.toolCallId);
|
|
1801
|
+
}
|
|
1802
|
+
processingToolResults.delete(part.toolCallId);
|
|
1803
|
+
});
|
|
1897
1804
|
}
|
|
1898
1805
|
}
|
|
1899
1806
|
}
|
|
@@ -2014,11 +1921,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
2014
1921
|
const aiChatbotX = injectStrict(AiChatbotXKey);
|
|
2015
1922
|
const getVoiceConfig = () => {
|
|
2016
1923
|
if (props.voiceConfig) return props.voiceConfig;
|
|
2017
|
-
|
|
2018
|
-
return aiChatbotX.voiceConfig();
|
|
2019
|
-
} catch {
|
|
2020
|
-
return null;
|
|
2021
|
-
}
|
|
1924
|
+
return null;
|
|
2022
1925
|
};
|
|
2023
1926
|
const endpoint = computed(() => {
|
|
2024
1927
|
return props.invokeUrl || "http://localhost:3001/agent/zyy55sw40nrl801056m0o/stream-invoke";
|
|
@@ -2087,11 +1990,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
2087
1990
|
const { voiceStatus, transcriptionText, wakeAnimating, isTranscribing } = voice;
|
|
2088
1991
|
const { isInvoking, currentTextContent, currentToolParts, executingTools, hasAnyContent, toolDisplayName } = agent;
|
|
2089
1992
|
aiChatbotX?.registerVoiceMethods({
|
|
2090
|
-
|
|
2091
|
-
stop: () => toggleVoiceMode(false),
|
|
2092
|
-
openDialog: async () => Promise.resolve(),
|
|
2093
|
-
closeDialog: async () => Promise.resolve(),
|
|
2094
|
-
toggleCollapse: async () => Promise.resolve()
|
|
1993
|
+
stopBroadcast: async () => interruptCurrentResponse()
|
|
2095
1994
|
});
|
|
2096
1995
|
onBeforeUnmount(async () => {
|
|
2097
1996
|
bubble.destroy();
|
|
@@ -2230,7 +2129,18 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
2230
2129
|
}
|
|
2231
2130
|
});
|
|
2232
2131
|
|
|
2233
|
-
const voiceAssistant = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
2132
|
+
const voiceAssistant = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-fda883a9"]]);
|
|
2133
|
+
|
|
2134
|
+
var clientCommandKey = /* @__PURE__ */ ((clientCommandKey2) => {
|
|
2135
|
+
clientCommandKey2["SET_THEME"] = "SiMeAgent_setTheme";
|
|
2136
|
+
clientCommandKey2["APPEND_MESSAGE"] = "SiMeAgent_appendMessage";
|
|
2137
|
+
clientCommandKey2["WAKE"] = "SiMeAgent_wake";
|
|
2138
|
+
clientCommandKey2["TRANSITION"] = "SiMeAgent_transition";
|
|
2139
|
+
clientCommandKey2["TRANSITION_END"] = "SiMeAgent_transition_end";
|
|
2140
|
+
clientCommandKey2["START_NEW_CONVERSATION"] = "SiMeAgent_startNewConversation";
|
|
2141
|
+
clientCommandKey2["RECOGNITION"] = "SiMeAgent_recognition";
|
|
2142
|
+
return clientCommandKey2;
|
|
2143
|
+
})(clientCommandKey || {});
|
|
2234
2144
|
|
|
2235
|
-
export { _sfc_main$
|
|
2145
|
+
export { AgentChatTransport, aiChat as AiChat, _sfc_main$1 as AiChatbotProvider, voiceAssistant as AiChatbotVoiceAssistant, AiChatbotXKey, clientCommandKey, createAgentChatTransport, injectStrict };
|
|
2236
2146
|
//# sourceMappingURL=sime-x-vue.mjs.map
|