@siact/sime-x-vue 0.0.17 → 0.0.19

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.
@@ -107,7 +107,7 @@
107
107
  const _hoisted_10$2 = { class: "ai-chat__messages-inner" };
108
108
  const _hoisted_11$2 = { class: "ai-chat__message-content" };
109
109
  const _hoisted_12$2 = ["innerHTML"];
110
- const _hoisted_13$2 = {
110
+ const _hoisted_13$1 = {
111
111
  key: 1,
112
112
  class: "ai-chat__reasoning"
113
113
  };
@@ -470,7 +470,7 @@
470
470
  innerHTML: renderMarkdown(part.text)
471
471
  }, null, 8, _hoisted_12$2)
472
472
  ])
473
- ], 2)) : part.type === "reasoning" ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_13$2, [
473
+ ], 2)) : part.type === "reasoning" ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_13$1, [
474
474
  vue.createElementVNode("button", {
475
475
  class: "ai-chat__reasoning-trigger",
476
476
  onClick: ($event) => toggleReasoning(message.id)
@@ -1465,7 +1465,7 @@
1465
1465
  };
1466
1466
  const _hoisted_11$1 = { class: "input-bar" };
1467
1467
  const _hoisted_12$1 = ["disabled"];
1468
- const _hoisted_13$1 = ["disabled"];
1468
+ const _hoisted_13 = ["disabled"];
1469
1469
  const _hoisted_14 = {
1470
1470
  key: 0,
1471
1471
  class: "btn-spinner",
@@ -1512,6 +1512,12 @@
1512
1512
  scrollToBottom: () => {
1513
1513
  }
1514
1514
  };
1515
+ const test = () => {
1516
+ aiChatbotX.invokeText("你好");
1517
+ };
1518
+ const test1 = () => {
1519
+ aiChatbotX.abortInvoke();
1520
+ };
1515
1521
  const endpoint = `/sime/proxy/organizations/${aiChatbotX.organizationId()}/agents/${props.agentId}/stream-invoke`;
1516
1522
  const agent = useAgentInvoke({
1517
1523
  endpoint,
@@ -1550,6 +1556,8 @@
1550
1556
  class: "command-test",
1551
1557
  "data-theme": currentTheme$1
1552
1558
  }, [
1559
+ vue.createElementVNode("button", { onClick: test }, "测试"),
1560
+ vue.createElementVNode("button", { onClick: test1 }, "测试1"),
1553
1561
  vue.createVNode(vue.Transition, { name: "bubble-fade" }, {
1554
1562
  default: vue.withCtx(() => [
1555
1563
  vue.unref(showBubble) ? (vue.openBlock(), vue.createElementBlock("div", {
@@ -1664,14 +1672,14 @@
1664
1672
  "stroke-linejoin": "round"
1665
1673
  }, null, -1)
1666
1674
  ])]))
1667
- ], 8, _hoisted_13$1)
1675
+ ], 8, _hoisted_13)
1668
1676
  ])
1669
1677
  ]);
1670
1678
  };
1671
1679
  }
1672
1680
  });
1673
1681
 
1674
- const commandTest = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-5c7468c4"]]);
1682
+ const commandTest = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-93ed98f5"]]);
1675
1683
 
1676
1684
  class CommandManager {
1677
1685
  commands = /* @__PURE__ */ new Map();
@@ -1732,7 +1740,8 @@
1732
1740
  __name: "sime-provider",
1733
1741
  props: {
1734
1742
  appToken: {},
1735
- organizationId: {}
1743
+ organizationId: {},
1744
+ projectId: {}
1736
1745
  },
1737
1746
  setup(__props) {
1738
1747
  const props = __props;
@@ -1743,6 +1752,9 @@
1743
1752
  });
1744
1753
  const stopBroadcastRef = vue.shallowRef(async () => {
1745
1754
  });
1755
+ const voiceAssistantRef = vue.shallowRef(
1756
+ null
1757
+ );
1746
1758
  vue.provide(AiChatbotXKey, {
1747
1759
  appToken: () => props.appToken,
1748
1760
  organizationId: () => props.organizationId,
@@ -1754,6 +1766,9 @@
1754
1766
  if (methods.start) startListeningRef.value = methods.start;
1755
1767
  if (methods.stop) stopListeningRef.value = methods.stop;
1756
1768
  },
1769
+ registerVoiceAssistant: (methods) => {
1770
+ voiceAssistantRef.value = methods;
1771
+ },
1757
1772
  getCommads: async () => commandManager.value.getCommands(),
1758
1773
  registerCommand: (cmd) => {
1759
1774
  commandManager.value.registerCommand(cmd);
@@ -1763,6 +1778,12 @@
1763
1778
  },
1764
1779
  async executeCommand(commandName, args = []) {
1765
1780
  return await commandManager.value.executeCommand(commandName, args);
1781
+ },
1782
+ invokeText: async (text) => {
1783
+ voiceAssistantRef.value?.agentInvoke(text);
1784
+ },
1785
+ abortInvoke: () => {
1786
+ voiceAssistantRef.value?.agentAbort();
1766
1787
  }
1767
1788
  });
1768
1789
  return (_ctx, _cache) => {
@@ -1778,8 +1799,47 @@
1778
1799
  let initPromise = null;
1779
1800
  let audioCtx = null;
1780
1801
  let sentenceBuffer = "";
1781
- const sentenceDelimiters = /[。!?;\n.!?;]/;
1782
- const stripMarkdown = (text) => text.replace(/```[\s\S]*?```/g, "").replace(/\|[^\n]*\|/g, "").replace(/#{1,6}\s*/g, "").replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1").replace(/`([^`]*)`/g, "$1").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/^[-*+]\s+/gm, "").replace(/^>\s+/gm, "").replace(/\s*\+\s*/g, "加").replace(/\s*-\s*/g, "减").replace(/\s*×\s*/g, "乘").replace(/\s*÷\s*/g, "除").replace(/\s*=\s*/g, "等于").replace(/(\d)\.(\d)/g, "$1点$2").replace(/\n{2,}/g, "。").replace(/\n/g, ",").trim();
1802
+ const SENTENCE_DELIMITERS = /[。!?;\n!?;]/;
1803
+ const LEADING_WEAK_PUNCTUATION = /^[\s,、;:,.!?。]+/;
1804
+ const TRAILING_SENTENCE_PUNCTUATION = /[。!?.!?;;]$/;
1805
+ const findSentenceBoundary = (text) => {
1806
+ for (let i = 0; i < text.length; i++) {
1807
+ const char = text[i];
1808
+ if (SENTENCE_DELIMITERS.test(char)) {
1809
+ return i;
1810
+ }
1811
+ }
1812
+ return -1;
1813
+ };
1814
+ const normalizeNumbers = (text) => {
1815
+ return text.replace(/(-?\d[\d,]*\.?\d*)\s*%/g, (_, num) => {
1816
+ const cleanNum = num.replace(/,/g, "");
1817
+ const n = parseFloat(cleanNum);
1818
+ const spoken = cleanNum.replace(".", "点");
1819
+ if (n < 0) return `负百分之${spoken.replace("-", "")}`;
1820
+ return `百分之${spoken}`;
1821
+ }).replace(/(-?\d[\d,]*)\.(\d+)/g, (_, intPart, decPart) => {
1822
+ const cleanInt = intPart.replace(/,/g, "");
1823
+ if (cleanInt.startsWith("-")) {
1824
+ return `负${cleanInt.slice(1)}点${decPart}`;
1825
+ }
1826
+ return `${cleanInt}点${decPart}`;
1827
+ }).replace(/-(\d)/g, "负$1");
1828
+ };
1829
+ const stripMarkdown = (text) => {
1830
+ let result = text.replace(/```[\s\S]*?```/g, "").replace(/\|[^\n]*\|/g, "").replace(/#{1,6}\s*/g, "").replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1").replace(/`([^`]*)`/g, "$1").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/^[-*+]\s+/gm, "").replace(/^>\s+/gm, "");
1831
+ result = normalizeNumbers(result);
1832
+ result = result.replace(/\s*\+\s*(?=\d)/g, "加").replace(/\s*\+\s*/g, "加").replace(/\s*-\s*(?=\d)/g, "减").replace(/\s*-\s*/g, "减").replace(/\s*×\s*/g, "乘").replace(/\s*÷\s*/g, "除").replace(/\s*=\s*/g, "等于").replace(/\n{2,}/g, "。").replace(/\n/g, ",").trim();
1833
+ return result;
1834
+ };
1835
+ const normalizeSpeakText = (text, options) => {
1836
+ const clean = stripMarkdown(text).replace(LEADING_WEAK_PUNCTUATION, "").trim();
1837
+ if (!clean) return "";
1838
+ if (options?.isFinalChunk && !TRAILING_SENTENCE_PUNCTUATION.test(clean)) {
1839
+ return `${clean}。`;
1840
+ }
1841
+ return clean;
1842
+ };
1783
1843
  const warmUpAudio = () => {
1784
1844
  if (!audioCtx || audioCtx.state === "closed") {
1785
1845
  try {
@@ -1848,7 +1908,7 @@
1848
1908
  return initPromise;
1849
1909
  };
1850
1910
  const speak = async (text) => {
1851
- const clean = stripMarkdown(text);
1911
+ const clean = normalizeSpeakText(text);
1852
1912
  if (!clean.trim()) return;
1853
1913
  hasPendingAudio.value = true;
1854
1914
  const tts = await ensureInstance();
@@ -1862,17 +1922,20 @@
1862
1922
  const feed = (delta) => {
1863
1923
  sentenceBuffer += delta;
1864
1924
  while (true) {
1865
- const match = sentenceBuffer.match(sentenceDelimiters);
1866
- if (!match || match.index === void 0) break;
1867
- const sentence = sentenceBuffer.slice(0, match.index + 1).trim();
1868
- sentenceBuffer = sentenceBuffer.slice(match.index + 1);
1925
+ const boundaryIndex = findSentenceBoundary(sentenceBuffer);
1926
+ if (boundaryIndex === -1) break;
1927
+ const sentence = sentenceBuffer.slice(0, boundaryIndex + 1).trim();
1928
+ sentenceBuffer = sentenceBuffer.slice(boundaryIndex + 1);
1869
1929
  if (sentence.length > 0) speak(sentence);
1870
1930
  }
1871
1931
  };
1872
1932
  const flush = () => {
1873
1933
  const remaining = sentenceBuffer.trim();
1874
1934
  sentenceBuffer = "";
1875
- if (remaining.length > 0) speak(remaining);
1935
+ if (remaining.length > 0) {
1936
+ const clean = normalizeSpeakText(remaining, { isFinalChunk: true });
1937
+ if (clean) void speak(clean);
1938
+ }
1876
1939
  };
1877
1940
  const stop = () => {
1878
1941
  sentenceBuffer = "";
@@ -2188,12 +2251,8 @@
2188
2251
  key: 2,
2189
2252
  class: "agent-text"
2190
2253
  };
2191
- const _hoisted_11 = {
2192
- key: 0,
2193
- class: "status-pill"
2194
- };
2195
- const _hoisted_12 = { class: "fab-avatar-wrapper" };
2196
- const _hoisted_13 = ["src"];
2254
+ const _hoisted_11 = { class: "fab-avatar-wrapper" };
2255
+ const _hoisted_12 = ["src"];
2197
2256
  const currentTheme = "dark";
2198
2257
  const _sfc_main = /* @__PURE__ */ vue.defineComponent({
2199
2258
  __name: "voice-assistant",
@@ -2282,13 +2341,17 @@
2282
2341
  tts.warmUpAudio();
2283
2342
  await voice.toggleVoiceMode(targetState);
2284
2343
  };
2285
- const { voiceStatus, transcriptionText, wakeAnimating, isTranscribing } = voice;
2344
+ const { voiceStatus, wakeAnimating, isTranscribing } = voice;
2286
2345
  const { isInvoking, currentTextContent, currentToolParts, executingTools, hasAnyContent, toolDisplayName } = agent;
2287
2346
  aiChatbotX?.registerVoiceMethods({
2288
2347
  stopBroadcast: async () => interruptCurrentResponse(),
2289
2348
  start: () => toggleVoiceMode(true),
2290
2349
  stop: () => toggleVoiceMode(false)
2291
2350
  });
2351
+ aiChatbotX?.registerVoiceAssistant({
2352
+ agentInvoke: agent.invoke,
2353
+ agentAbort: agent.abort
2354
+ });
2292
2355
  vue.onBeforeUnmount(async () => {
2293
2356
  bubble.destroy();
2294
2357
  agent.abort();
@@ -2376,15 +2439,14 @@
2376
2439
  class: "assistant-fab",
2377
2440
  onClick: _cache[0] || (_cache[0] = ($event) => toggleVoiceMode())
2378
2441
  }, [
2379
- vue.unref(transcriptionText) || vue.unref(isInvoking) ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_11, vue.toDisplayString(vue.unref(isInvoking) ? "正在思考中..." : vue.unref(transcriptionText)), 1)) : vue.createCommentVNode("", true),
2380
- vue.createElementVNode("div", _hoisted_12, [
2442
+ vue.createElementVNode("div", _hoisted_11, [
2381
2443
  vue.createElementVNode("img", {
2382
2444
  src: __props.xLogo ? __props.xLogo : "/x.png",
2383
2445
  alt: "voice assistant",
2384
2446
  style: vue.normalizeStyle({
2385
2447
  width: `${__props.xSize?.width || 88}px`
2386
2448
  })
2387
- }, null, 12, _hoisted_13),
2449
+ }, null, 12, _hoisted_12),
2388
2450
  vue.createVNode(vue.Transition, { name: "indicator-fade" }, {
2389
2451
  default: vue.withCtx(() => [
2390
2452
  vue.unref(voiceStatus) === "listening" ? (vue.openBlock(), vue.createElementBlock("div", {
@@ -2426,7 +2488,7 @@
2426
2488
  }
2427
2489
  });
2428
2490
 
2429
- const voiceAssistant = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-59d72f34"]]);
2491
+ const voiceAssistant = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d1d9b351"]]);
2430
2492
 
2431
2493
  var clientCommandKey = /* @__PURE__ */ ((clientCommandKey2) => {
2432
2494
  clientCommandKey2["SET_THEME"] = "SiMeAgent_setTheme";