byt-lingxiao-ai 0.3.5 → 0.3.6
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/components/AiMessage.vue +26 -144
- package/components/ChatWindow.vue +1 -0
- package/components/config/index.js +5 -2
- package/components/mixins/audioMixin.js +17 -24
- package/components/mixins/messageMixin.js +5 -3
- package/components/mixins/webSocketMixin.js +6 -0
- package/components/utils/StreamParser.js +50 -229
- package/dist/index.common.js +92 -364
- package/dist/index.common.js.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.umd.js +92 -364
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/package.json +3 -2
package/dist/index.umd.js
CHANGED
|
@@ -28460,7 +28460,7 @@ if (typeof window !== 'undefined') {
|
|
|
28460
28460
|
var es_iterator_constructor = __webpack_require__(8111);
|
|
28461
28461
|
// EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.for-each.js
|
|
28462
28462
|
var es_iterator_for_each = __webpack_require__(7588);
|
|
28463
|
-
;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-82.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/templateLoader.js??ruleSet[1].rules[3]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/ChatWindow.vue?vue&type=template&id=
|
|
28463
|
+
;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-82.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/templateLoader.js??ruleSet[1].rules[3]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/ChatWindow.vue?vue&type=template&id=244aa1c1&scoped=true
|
|
28464
28464
|
var render = function render() {
|
|
28465
28465
|
var _vm = this,
|
|
28466
28466
|
_c = _vm._self._c;
|
|
@@ -29027,8 +29027,8 @@ var UserMessage_component = normalizeComponent(
|
|
|
29027
29027
|
)
|
|
29028
29028
|
|
|
29029
29029
|
/* harmony default export */ var UserMessage = (UserMessage_component.exports);
|
|
29030
|
-
;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-82.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/templateLoader.js??ruleSet[1].rules[3]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/AiMessage.vue?vue&type=template&id=
|
|
29031
|
-
var
|
|
29030
|
+
;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-82.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/templateLoader.js??ruleSet[1].rules[3]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/AiMessage.vue?vue&type=template&id=6cbccb01&scoped=true
|
|
29031
|
+
var AiMessagevue_type_template_id_6cbccb01_scoped_true_render = function render() {
|
|
29032
29032
|
var _vm = this,
|
|
29033
29033
|
_c = _vm._self._c;
|
|
29034
29034
|
return _c('div', {
|
|
@@ -29061,10 +29061,8 @@ var AiMessagevue_type_template_id_2422d7c0_scoped_true_render = function render(
|
|
|
29061
29061
|
}
|
|
29062
29062
|
})])]);
|
|
29063
29063
|
};
|
|
29064
|
-
var
|
|
29064
|
+
var AiMessagevue_type_template_id_6cbccb01_scoped_true_staticRenderFns = [];
|
|
29065
29065
|
|
|
29066
|
-
// EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.filter.js
|
|
29067
|
-
var es_iterator_filter = __webpack_require__(2489);
|
|
29068
29066
|
;// ./node_modules/@babel/runtime/helpers/esm/typeof.js
|
|
29069
29067
|
function _typeof(o) {
|
|
29070
29068
|
"@babel/helpers - typeof";
|
|
@@ -29108,6 +29106,8 @@ function _defineProperty(e, r, t) {
|
|
|
29108
29106
|
}) : e[r] = t, e;
|
|
29109
29107
|
}
|
|
29110
29108
|
|
|
29109
|
+
// EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.filter.js
|
|
29110
|
+
var es_iterator_filter = __webpack_require__(2489);
|
|
29111
29111
|
// EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.map.js
|
|
29112
29112
|
var es_iterator_map = __webpack_require__(1701);
|
|
29113
29113
|
// EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.some.js
|
|
@@ -30837,10 +30837,6 @@ var lib = __webpack_require__(336);
|
|
|
30837
30837
|
|
|
30838
30838
|
|
|
30839
30839
|
|
|
30840
|
-
|
|
30841
|
-
|
|
30842
|
-
|
|
30843
|
-
|
|
30844
30840
|
d.setOptions({
|
|
30845
30841
|
highlight: function (code, lang) {
|
|
30846
30842
|
if (lang && es.getLanguage(lang)) {
|
|
@@ -30853,115 +30849,6 @@ d.setOptions({
|
|
|
30853
30849
|
breaks: true,
|
|
30854
30850
|
gfm: true
|
|
30855
30851
|
});
|
|
30856
|
-
function parseMarkdown(text) {
|
|
30857
|
-
if (!text) return '';
|
|
30858
|
-
let html = text;
|
|
30859
|
-
html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
|
|
30860
|
-
return `<pre><code class="language-${lang || 'text'}">${escapeHtml(code.trim())}</code></pre>`;
|
|
30861
|
-
});
|
|
30862
|
-
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
30863
|
-
html = parseTable(html);
|
|
30864
|
-
html = html.replace(/^#### (.*$)/gm, '<h4>$1</h4>');
|
|
30865
|
-
html = html.replace(/^### (.*$)/gm, '<h3>$1</h3>');
|
|
30866
|
-
html = html.replace(/^## (.*$)/gm, '<h2>$1</h2>');
|
|
30867
|
-
html = html.replace(/^# (.*$)/gm, '<h1>$1</h1>');
|
|
30868
|
-
html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
|
|
30869
|
-
html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');
|
|
30870
|
-
html = html.replace(/~~(.*?)~~/g, '<del>$1</del>');
|
|
30871
|
-
html = html.replace(/^\s*[-*+]\s+(.+)$/gm, '<li>$1</li>');
|
|
30872
|
-
html = html.replace(/(<li>.*?<\/li>)/gs, '<ul>$1</ul>');
|
|
30873
|
-
html = html.replace(/^\s*\d+\.\s+(.+)$/gm, '<li>$1</li>');
|
|
30874
|
-
html = html.replace(/^>\s+(.+)$/gm, '<blockquote>$1</blockquote>');
|
|
30875
|
-
// 解析Markdown链接
|
|
30876
|
-
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" aria-current="page">$1</a>');
|
|
30877
|
-
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" />');
|
|
30878
|
-
html = html.replace(/^-{3,}$/gm, '<hr>');
|
|
30879
|
-
// 解析简单URL
|
|
30880
|
-
// const simpleUrlRegex = /(https?:\/\/[^\s<>"']+)/g;
|
|
30881
|
-
// html = html.replace(simpleUrlRegex, (match) => {
|
|
30882
|
-
// return `<a href="${match}" aria-current="page">${match}</a>`;
|
|
30883
|
-
// });
|
|
30884
|
-
|
|
30885
|
-
// html = html.replace(/\n/g, '<br>');
|
|
30886
|
-
|
|
30887
|
-
return html;
|
|
30888
|
-
}
|
|
30889
|
-
// 解析表格
|
|
30890
|
-
function parseTable(text) {
|
|
30891
|
-
const lines = text.split('\n');
|
|
30892
|
-
let result = [];
|
|
30893
|
-
let inTable = false;
|
|
30894
|
-
let tableRows = [];
|
|
30895
|
-
for (let i = 0; i < lines.length; i++) {
|
|
30896
|
-
const line = lines[i].trim();
|
|
30897
|
-
|
|
30898
|
-
// 检测表格行
|
|
30899
|
-
if (line.includes('|') && line.split('|').length >= 3) {
|
|
30900
|
-
if (!inTable) {
|
|
30901
|
-
inTable = true;
|
|
30902
|
-
tableRows = [];
|
|
30903
|
-
}
|
|
30904
|
-
tableRows.push(line);
|
|
30905
|
-
|
|
30906
|
-
// 检查下一行是否还是表格
|
|
30907
|
-
if (i === lines.length - 1 || !lines[i + 1].includes('|')) {
|
|
30908
|
-
// 表格结束
|
|
30909
|
-
result.push(renderTable(tableRows));
|
|
30910
|
-
inTable = false;
|
|
30911
|
-
tableRows = [];
|
|
30912
|
-
}
|
|
30913
|
-
} else {
|
|
30914
|
-
if (inTable) {
|
|
30915
|
-
// 表格意外结束
|
|
30916
|
-
result.push(renderTable(tableRows));
|
|
30917
|
-
inTable = false;
|
|
30918
|
-
tableRows = [];
|
|
30919
|
-
}
|
|
30920
|
-
result.push(line);
|
|
30921
|
-
}
|
|
30922
|
-
}
|
|
30923
|
-
return result.join('\n');
|
|
30924
|
-
}
|
|
30925
|
-
// 渲染表格
|
|
30926
|
-
function renderTable(rows) {
|
|
30927
|
-
if (rows.length < 2) return rows.join('\n');
|
|
30928
|
-
let html = '<div class="table-wrapper"><table class="markdown-table">';
|
|
30929
|
-
|
|
30930
|
-
// 表头
|
|
30931
|
-
const headerCells = rows[0].split('|').filter(cell => cell.trim());
|
|
30932
|
-
html += '<thead><tr>';
|
|
30933
|
-
headerCells.forEach(cell => {
|
|
30934
|
-
html += `<th>${cell.trim()}</th>`;
|
|
30935
|
-
});
|
|
30936
|
-
html += '</tr></thead>';
|
|
30937
|
-
|
|
30938
|
-
// 表体(跳过分隔行)
|
|
30939
|
-
html += '<tbody>';
|
|
30940
|
-
for (let i = 2; i < rows.length; i++) {
|
|
30941
|
-
const cells = rows[i].split('|').filter(cell => cell.trim());
|
|
30942
|
-
if (cells.length > 0) {
|
|
30943
|
-
html += '<tr>';
|
|
30944
|
-
cells.forEach(cell => {
|
|
30945
|
-
html += `<td><div class="table-cell">${cell.trim()}</div></td>`;
|
|
30946
|
-
});
|
|
30947
|
-
html += '</tr>';
|
|
30948
|
-
}
|
|
30949
|
-
}
|
|
30950
|
-
html += '</tbody>';
|
|
30951
|
-
html += '</table></div>';
|
|
30952
|
-
return html;
|
|
30953
|
-
}
|
|
30954
|
-
// HTML 转义
|
|
30955
|
-
function escapeHtml(text) {
|
|
30956
|
-
const map = {
|
|
30957
|
-
'&': '&',
|
|
30958
|
-
'<': '<',
|
|
30959
|
-
'>': '>',
|
|
30960
|
-
'"': '"',
|
|
30961
|
-
"'": '''
|
|
30962
|
-
};
|
|
30963
|
-
return text.replace(/[&<>"']/g, m => map[m]);
|
|
30964
|
-
}
|
|
30965
30852
|
/* harmony default export */ var AiMessagevue_type_script_lang_js = ({
|
|
30966
30853
|
name: 'AiMessage',
|
|
30967
30854
|
props: {
|
|
@@ -30975,7 +30862,7 @@ function escapeHtml(text) {
|
|
|
30975
30862
|
return this.message.thinkingExpanded !== false;
|
|
30976
30863
|
},
|
|
30977
30864
|
renderedContent() {
|
|
30978
|
-
return
|
|
30865
|
+
return d.parse(this.message.content || '');
|
|
30979
30866
|
},
|
|
30980
30867
|
isLoading() {
|
|
30981
30868
|
return this.message.loading === true;
|
|
@@ -30989,10 +30876,10 @@ function escapeHtml(text) {
|
|
|
30989
30876
|
});
|
|
30990
30877
|
;// ./components/AiMessage.vue?vue&type=script&lang=js
|
|
30991
30878
|
/* harmony default export */ var components_AiMessagevue_type_script_lang_js = (AiMessagevue_type_script_lang_js);
|
|
30992
|
-
;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-54.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-54.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-54.use[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/AiMessage.vue?vue&type=style&index=0&id=
|
|
30879
|
+
;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-54.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-54.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-54.use[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/AiMessage.vue?vue&type=style&index=0&id=6cbccb01&prod&scoped=true&lang=css
|
|
30993
30880
|
// extracted by mini-css-extract-plugin
|
|
30994
30881
|
|
|
30995
|
-
;// ./components/AiMessage.vue?vue&type=style&index=0&id=
|
|
30882
|
+
;// ./components/AiMessage.vue?vue&type=style&index=0&id=6cbccb01&prod&scoped=true&lang=css
|
|
30996
30883
|
|
|
30997
30884
|
;// ./components/AiMessage.vue
|
|
30998
30885
|
|
|
@@ -31005,11 +30892,11 @@ function escapeHtml(text) {
|
|
|
31005
30892
|
|
|
31006
30893
|
var AiMessage_component = normalizeComponent(
|
|
31007
30894
|
components_AiMessagevue_type_script_lang_js,
|
|
31008
|
-
|
|
31009
|
-
|
|
30895
|
+
AiMessagevue_type_template_id_6cbccb01_scoped_true_render,
|
|
30896
|
+
AiMessagevue_type_template_id_6cbccb01_scoped_true_staticRenderFns,
|
|
31010
30897
|
false,
|
|
31011
30898
|
null,
|
|
31012
|
-
"
|
|
30899
|
+
"6cbccb01",
|
|
31013
30900
|
null
|
|
31014
30901
|
|
|
31015
30902
|
)
|
|
@@ -31307,9 +31194,7 @@ var es_typed_array_with = __webpack_require__(9577);
|
|
|
31307
31194
|
audioContext: null,
|
|
31308
31195
|
microphone: null,
|
|
31309
31196
|
processor: null,
|
|
31310
|
-
audioBuffer: new Float32Array(0)
|
|
31311
|
-
// 优化
|
|
31312
|
-
int16Buffer: null
|
|
31197
|
+
audioBuffer: new Float32Array(0)
|
|
31313
31198
|
};
|
|
31314
31199
|
},
|
|
31315
31200
|
methods: {
|
|
@@ -31321,21 +31206,16 @@ var es_typed_array_with = __webpack_require__(9577);
|
|
|
31321
31206
|
audio: {
|
|
31322
31207
|
sampleRate: this.SAMPLE_RATE,
|
|
31323
31208
|
channelCount: 1,
|
|
31324
|
-
noiseSuppression:
|
|
31325
|
-
echoCancellation:
|
|
31209
|
+
noiseSuppression: false,
|
|
31210
|
+
echoCancellation: false,
|
|
31211
|
+
autoGainControl: false
|
|
31326
31212
|
}
|
|
31327
31213
|
});
|
|
31328
31214
|
this.audioContext = new AudioContext({
|
|
31329
31215
|
sampleRate: this.SAMPLE_RATE
|
|
31330
31216
|
});
|
|
31331
|
-
|
|
31332
|
-
// 优化 处理 AudioContext 挂起
|
|
31333
|
-
if (this.audioContext.state === 'suspended') {
|
|
31334
|
-
await this.audioContext.resume();
|
|
31335
|
-
}
|
|
31336
31217
|
this.microphone = this.audioContext.createMediaStreamSource(stream);
|
|
31337
31218
|
this.processor = this.audioContext.createScriptProcessor(this.FRAME_SIZE, 1, 1);
|
|
31338
|
-
this.int16Buffer = new Int16Array(this.FRAME_SIZE);
|
|
31339
31219
|
this.processor.onaudioprocess = this.processAudio;
|
|
31340
31220
|
this.microphone.connect(this.processor);
|
|
31341
31221
|
this.processor.connect(this.audioContext.destination);
|
|
@@ -31350,19 +31230,17 @@ var es_typed_array_with = __webpack_require__(9577);
|
|
|
31350
31230
|
processAudio(event) {
|
|
31351
31231
|
if (!this.isRecording) return;
|
|
31352
31232
|
const inputData = event.inputBuffer.getChannelData(0);
|
|
31353
|
-
|
|
31354
|
-
|
|
31355
|
-
|
|
31233
|
+
const tempBuffer = new Float32Array(this.audioBuffer.length + inputData.length);
|
|
31234
|
+
tempBuffer.set(this.audioBuffer, 0);
|
|
31235
|
+
tempBuffer.set(inputData, this.audioBuffer.length);
|
|
31236
|
+
this.audioBuffer = tempBuffer;
|
|
31237
|
+
while (this.audioBuffer.length >= this.FRAME_SIZE) {
|
|
31238
|
+
const frame = this.audioBuffer.slice(0, this.FRAME_SIZE);
|
|
31239
|
+
this.audioBuffer = this.audioBuffer.slice(this.FRAME_SIZE);
|
|
31240
|
+
const pcmData = this.floatTo16BitPCM(frame);
|
|
31241
|
+
if (this.ws && this.ws.readyState === this.ws.OPEN) {
|
|
31242
|
+
this.ws.send(pcmData);
|
|
31356
31243
|
}
|
|
31357
|
-
for (let i = 0; i < inputData.length; i++) {
|
|
31358
|
-
let s = inputData[i];
|
|
31359
|
-
// 简单的 clamp
|
|
31360
|
-
s = s < -1 ? -1 : s > 1 ? 1 : s;
|
|
31361
|
-
this.int16Buffer[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
|
|
31362
|
-
}
|
|
31363
|
-
|
|
31364
|
-
// 4. 发送数据
|
|
31365
|
-
this.ws.send(this.int16Buffer.buffer);
|
|
31366
31244
|
}
|
|
31367
31245
|
},
|
|
31368
31246
|
floatTo16BitPCM(input) {
|
|
@@ -31390,6 +31268,7 @@ var es_typed_array_with = __webpack_require__(9577);
|
|
|
31390
31268
|
this.$refs.audioPlayer.play();
|
|
31391
31269
|
},
|
|
31392
31270
|
pause() {
|
|
31271
|
+
console.log('暂停播放');
|
|
31393
31272
|
this.robotStatus = 'waiting';
|
|
31394
31273
|
this.$refs.audioPlayer.pause();
|
|
31395
31274
|
},
|
|
@@ -31415,17 +31294,21 @@ var es_typed_array_with = __webpack_require__(9577);
|
|
|
31415
31294
|
this.pause();
|
|
31416
31295
|
} else if (command === 'C6') {
|
|
31417
31296
|
this.robotStatus = 'leaving';
|
|
31297
|
+
this.avaterStatus = 'normal';
|
|
31418
31298
|
this.stop();
|
|
31419
31299
|
}
|
|
31420
31300
|
}
|
|
31421
31301
|
}
|
|
31422
31302
|
});
|
|
31423
31303
|
;// ./components/config/index.js
|
|
31424
|
-
const
|
|
31304
|
+
const protocol = window.location.protocol;
|
|
31305
|
+
const host = window.location.hostname;
|
|
31306
|
+
const baseUrl = `${protocol}//${host}`;
|
|
31425
31307
|
const chatPort = '3100';
|
|
31308
|
+
const voicePort = '3101';
|
|
31426
31309
|
console.log(baseUrl, chatPort);
|
|
31427
31310
|
const API_URL = `${baseUrl}:${chatPort}/lingxiao-byt/api/v1/mcp/ask`;
|
|
31428
|
-
const WS_URL =
|
|
31311
|
+
const WS_URL = `ws://${host}:${voicePort}/ai_model/ws/voice-stream`;
|
|
31429
31312
|
const AUDIO_URL = '/minio/lingxiaoai/byt.mp3';
|
|
31430
31313
|
const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json';
|
|
31431
31314
|
;// ./components/mixins/webSocketMixin.js
|
|
@@ -31501,6 +31384,10 @@ const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json';
|
|
|
31501
31384
|
handleWebSocketMessage(data) {
|
|
31502
31385
|
if (data.type === 'detection') {
|
|
31503
31386
|
console.log('检测到唤醒词');
|
|
31387
|
+
console.log('当前状态:', this.avaterStatus);
|
|
31388
|
+
if (this.robotStatus === 'speaking') {
|
|
31389
|
+
this.robotStatus = 'waiting';
|
|
31390
|
+
}
|
|
31504
31391
|
this.avaterStatus = 'normal';
|
|
31505
31392
|
|
|
31506
31393
|
// 性能检测起点
|
|
@@ -31521,6 +31408,8 @@ const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json';
|
|
|
31521
31408
|
this.analyzeAudioCommand(data.category);
|
|
31522
31409
|
} else {
|
|
31523
31410
|
console.log('状态: 其他');
|
|
31411
|
+
this.avaterStatus = 'normal';
|
|
31412
|
+
this.robotStatus = 'leaving';
|
|
31524
31413
|
}
|
|
31525
31414
|
},
|
|
31526
31415
|
closeWebSocket() {
|
|
@@ -31532,222 +31421,99 @@ const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json';
|
|
|
31532
31421
|
});
|
|
31533
31422
|
;// ./components/utils/StreamParser.js
|
|
31534
31423
|
|
|
31535
|
-
|
|
31536
31424
|
class StreamParser {
|
|
31537
31425
|
constructor(options = {}) {
|
|
31538
31426
|
this.options = {
|
|
31539
31427
|
updateInterval: 16,
|
|
31540
|
-
//
|
|
31541
|
-
batchSize: 100,
|
|
31542
|
-
// 批处理大小
|
|
31428
|
+
// 每16ms刷新一次(约60fps)
|
|
31543
31429
|
debug: false,
|
|
31544
|
-
// 是否开启调试
|
|
31545
31430
|
...options
|
|
31546
31431
|
};
|
|
31547
31432
|
this.reset();
|
|
31548
31433
|
}
|
|
31549
31434
|
|
|
31550
|
-
|
|
31551
|
-
* 重置解析器状态
|
|
31552
|
-
*/
|
|
31435
|
+
// 重置解析器状态
|
|
31553
31436
|
reset() {
|
|
31554
31437
|
this.buffer = '';
|
|
31555
|
-
this.contentBuffer = [];
|
|
31438
|
+
this.contentBuffer = [];
|
|
31556
31439
|
this.thinkingBuffer = [];
|
|
31557
31440
|
this.inTag = false;
|
|
31558
31441
|
this.tagBuffer = '';
|
|
31559
31442
|
this.updateTimer = null;
|
|
31560
31443
|
this.status = 'output'; // thinking | output
|
|
31561
|
-
this.metrics = {
|
|
31562
|
-
startTime: Date.now(),
|
|
31563
|
-
chunks: 0,
|
|
31564
|
-
events: 0,
|
|
31565
|
-
chars: 0
|
|
31566
|
-
};
|
|
31567
31444
|
}
|
|
31568
31445
|
|
|
31569
|
-
|
|
31570
|
-
* 处理流数据块
|
|
31571
|
-
* @param {string} chunk - 接收到的数据块
|
|
31572
|
-
* @param {Function} callback - 更新回调函数
|
|
31573
|
-
*/
|
|
31446
|
+
// 处理接收到的流数据块
|
|
31574
31447
|
processChunk(chunk, callback) {
|
|
31575
|
-
|
|
31576
|
-
this.
|
|
31577
|
-
if (this.options.debug) {
|
|
31578
|
-
console.log('[StreamParser] 收到chunk:', chunk.substring(0, 100));
|
|
31579
|
-
}
|
|
31580
|
-
if (!this.buffer.includes('data:') && !this.buffer.includes('\n\n')) {
|
|
31581
|
-
// 纯文本流,直接处理
|
|
31582
|
-
if (this.options.debug) {
|
|
31583
|
-
console.log('[StreamParser] 检测到纯文本流');
|
|
31584
|
-
}
|
|
31585
|
-
this.processPlainText(callback);
|
|
31586
|
-
return;
|
|
31587
|
-
}
|
|
31588
|
-
|
|
31589
|
-
// 尝试解析为 SSE 格式
|
|
31590
|
-
if (this.buffer.includes('data:')) {
|
|
31591
|
-
this.processSSEFormat(callback);
|
|
31592
|
-
} else {
|
|
31593
|
-
// 直接处理纯文本流
|
|
31594
|
-
this.processPlainText(callback);
|
|
31595
|
-
}
|
|
31596
|
-
}
|
|
31597
|
-
|
|
31598
|
-
/**
|
|
31599
|
-
* 处理标准 SSE 格式
|
|
31600
|
-
*/
|
|
31601
|
-
processSSEFormat(callback) {
|
|
31602
|
-
// SSE 格式:事件由双换行符分隔
|
|
31603
|
-
const events = this.buffer.split('\n\n');
|
|
31604
|
-
|
|
31605
|
-
// 保留最后一个可能不完整的事件
|
|
31606
|
-
this.buffer = events.pop() || '';
|
|
31607
|
-
|
|
31608
|
-
// 处理完整的事件
|
|
31609
|
-
for (const event of events) {
|
|
31610
|
-
if (event.trim()) {
|
|
31611
|
-
this.metrics.events++;
|
|
31612
|
-
this.processSSEEvent(event, callback);
|
|
31613
|
-
}
|
|
31614
|
-
}
|
|
31448
|
+
if (!chunk) return;
|
|
31449
|
+
this.parseContent(chunk, callback);
|
|
31615
31450
|
}
|
|
31616
31451
|
|
|
31617
|
-
|
|
31618
|
-
* 处理纯文本流格式
|
|
31619
|
-
*/
|
|
31620
|
-
processPlainText(callback) {
|
|
31621
|
-
const content = this.buffer;
|
|
31622
|
-
this.buffer = ''; // 清空缓冲区
|
|
31623
|
-
|
|
31624
|
-
if (content) {
|
|
31625
|
-
this.metrics.chars += content.length;
|
|
31626
|
-
if (this.options.debug) {
|
|
31627
|
-
console.log('[StreamParser] 处理纯文本:', content);
|
|
31628
|
-
}
|
|
31629
|
-
this.parseContent(content, callback);
|
|
31630
|
-
}
|
|
31631
|
-
}
|
|
31632
|
-
|
|
31633
|
-
/**
|
|
31634
|
-
* 处理单个SSE事件
|
|
31635
|
-
*/
|
|
31636
|
-
processSSEEvent(eventStr, callback) {
|
|
31637
|
-
const lines = eventStr.split('\n');
|
|
31638
|
-
for (const line of lines) {
|
|
31639
|
-
// 解析 data: 行
|
|
31640
|
-
if (line.startsWith('data:')) {
|
|
31641
|
-
const data = line.substring(5).trim();
|
|
31642
|
-
if (data === '[DONE]') {
|
|
31643
|
-
this.flush(callback);
|
|
31644
|
-
return;
|
|
31645
|
-
}
|
|
31646
|
-
try {
|
|
31647
|
-
const parsed = JSON.parse(data);
|
|
31648
|
-
const content = parsed?.choices?.[0]?.delta?.content;
|
|
31649
|
-
if (content) {
|
|
31650
|
-
this.metrics.chars += content.length;
|
|
31651
|
-
if (this.options.debug) {
|
|
31652
|
-
console.log('[StreamParser] 解析SSE内容:', content);
|
|
31653
|
-
}
|
|
31654
|
-
this.parseContent(content, callback);
|
|
31655
|
-
}
|
|
31656
|
-
} catch (error) {
|
|
31657
|
-
if (this.options.debug) {
|
|
31658
|
-
console.warn('[StreamParser] JSON解析失败:', data, error);
|
|
31659
|
-
}
|
|
31660
|
-
}
|
|
31661
|
-
}
|
|
31662
|
-
}
|
|
31663
|
-
}
|
|
31664
|
-
|
|
31665
|
-
/**
|
|
31666
|
-
* 使用状态机解析内容和标签
|
|
31667
|
-
*/
|
|
31452
|
+
// 核心内容解析,支持 <think> 标签
|
|
31668
31453
|
parseContent(content, callback) {
|
|
31669
31454
|
let i = 0;
|
|
31670
31455
|
while (i < content.length) {
|
|
31671
31456
|
if (this.inTag) {
|
|
31672
|
-
// 在标签内部,查找标签结束
|
|
31673
31457
|
const endIndex = content.indexOf('>', i);
|
|
31674
31458
|
if (endIndex !== -1) {
|
|
31675
|
-
// 找到标签结束
|
|
31676
31459
|
this.tagBuffer += content.substring(i, endIndex + 1);
|
|
31677
31460
|
this.handleTag(this.tagBuffer);
|
|
31678
31461
|
this.tagBuffer = '';
|
|
31679
31462
|
this.inTag = false;
|
|
31680
31463
|
i = endIndex + 1;
|
|
31681
31464
|
} else {
|
|
31682
|
-
//
|
|
31465
|
+
// 标签未闭合,超过50字符强制输出,防止阻塞
|
|
31683
31466
|
this.tagBuffer += content.substring(i);
|
|
31467
|
+
if (this.tagBuffer.length > 50) {
|
|
31468
|
+
this.appendText(this.tagBuffer);
|
|
31469
|
+
this.tagBuffer = '';
|
|
31470
|
+
this.inTag = false;
|
|
31471
|
+
}
|
|
31684
31472
|
break;
|
|
31685
31473
|
}
|
|
31686
31474
|
} else {
|
|
31687
|
-
// 不在标签内,查找标签开始
|
|
31688
31475
|
const startIndex = content.indexOf('<', i);
|
|
31689
31476
|
if (startIndex !== -1) {
|
|
31690
|
-
|
|
31691
|
-
if (startIndex > i) {
|
|
31692
|
-
this.appendText(content.substring(i, startIndex));
|
|
31693
|
-
}
|
|
31694
|
-
|
|
31695
|
-
// 检查是否是完整标签
|
|
31477
|
+
if (startIndex > i) this.appendText(content.substring(i, startIndex));
|
|
31696
31478
|
const endIndex = content.indexOf('>', startIndex);
|
|
31697
31479
|
if (endIndex !== -1) {
|
|
31698
|
-
// 完整标签
|
|
31699
31480
|
const tag = content.substring(startIndex, endIndex + 1);
|
|
31700
31481
|
this.handleTag(tag);
|
|
31701
31482
|
i = endIndex + 1;
|
|
31702
31483
|
} else {
|
|
31703
|
-
|
|
31704
|
-
|
|
31705
|
-
|
|
31706
|
-
|
|
31484
|
+
const nextChar = content[startIndex + 1];
|
|
31485
|
+
if (!/[a-zA-Z/]/.test(nextChar)) {
|
|
31486
|
+
// 很可能不是标签,直接当文本输出
|
|
31487
|
+
this.appendText('<');
|
|
31488
|
+
i = startIndex + 1;
|
|
31489
|
+
} else {
|
|
31490
|
+
this.inTag = true;
|
|
31491
|
+
this.tagBuffer = content.substring(startIndex);
|
|
31492
|
+
break;
|
|
31493
|
+
}
|
|
31707
31494
|
}
|
|
31708
31495
|
} else {
|
|
31709
|
-
// 没有标签,全部是文本
|
|
31710
31496
|
this.appendText(content.substring(i));
|
|
31711
31497
|
break;
|
|
31712
31498
|
}
|
|
31713
31499
|
}
|
|
31714
31500
|
}
|
|
31715
|
-
|
|
31716
|
-
// 定时批量更新
|
|
31717
31501
|
this.scheduleUpdate(callback);
|
|
31718
31502
|
}
|
|
31719
31503
|
|
|
31720
|
-
|
|
31721
|
-
* 处理标签
|
|
31722
|
-
*/
|
|
31504
|
+
// 处理标签
|
|
31723
31505
|
handleTag(tag) {
|
|
31724
|
-
const
|
|
31725
|
-
if (this.
|
|
31726
|
-
console.log('[StreamParser] 处理标签:', tag);
|
|
31727
|
-
}
|
|
31728
|
-
if (tagName === '<think>') {
|
|
31729
|
-
this.status = 'thinking';
|
|
31730
|
-
} else if (tagName === '</think>') {
|
|
31731
|
-
this.status = 'output';
|
|
31732
|
-
}
|
|
31733
|
-
// 可扩展:支持更多标签类型
|
|
31506
|
+
const t = tag.toLowerCase();
|
|
31507
|
+
if (t === '<think>') this.status = 'thinking';else if (t === '</think>') this.status = 'output';
|
|
31734
31508
|
}
|
|
31735
31509
|
|
|
31736
|
-
|
|
31737
|
-
* 添加文本到缓冲区
|
|
31738
|
-
*/
|
|
31510
|
+
// 添加文本到缓冲区
|
|
31739
31511
|
appendText(text) {
|
|
31740
31512
|
if (!text) return;
|
|
31741
|
-
if (this.status === 'thinking')
|
|
31742
|
-
this.thinkingBuffer.push(text);
|
|
31743
|
-
} else {
|
|
31744
|
-
this.contentBuffer.push(text);
|
|
31745
|
-
}
|
|
31513
|
+
if (this.status === 'thinking') this.thinkingBuffer.push(text);else this.contentBuffer.push(text);
|
|
31746
31514
|
}
|
|
31747
31515
|
|
|
31748
|
-
|
|
31749
|
-
* 计划更新(防抖)
|
|
31750
|
-
*/
|
|
31516
|
+
// 防抖刷新
|
|
31751
31517
|
scheduleUpdate(callback) {
|
|
31752
31518
|
if (this.updateTimer) return;
|
|
31753
31519
|
this.updateTimer = setTimeout(() => {
|
|
@@ -31756,74 +31522,33 @@ class StreamParser {
|
|
|
31756
31522
|
}, this.options.updateInterval);
|
|
31757
31523
|
}
|
|
31758
31524
|
|
|
31759
|
-
|
|
31760
|
-
* 立即刷新缓冲区
|
|
31761
|
-
*/
|
|
31525
|
+
// 刷新缓冲区
|
|
31762
31526
|
flush(callback) {
|
|
31763
|
-
if (!callback)
|
|
31764
|
-
console.warn('[StreamParser] flush: callback 为空');
|
|
31765
|
-
return;
|
|
31766
|
-
}
|
|
31767
|
-
const hasThinking = this.thinkingBuffer.length > 0;
|
|
31768
|
-
const hasContent = this.contentBuffer.length > 0;
|
|
31769
|
-
if (!hasThinking && !hasContent) return;
|
|
31770
|
-
|
|
31771
|
-
// 使用 join 比字符串拼接性能更好
|
|
31527
|
+
if (!callback) return;
|
|
31772
31528
|
const result = {
|
|
31773
|
-
thinking:
|
|
31774
|
-
content:
|
|
31529
|
+
thinking: this.thinkingBuffer.length ? this.thinkingBuffer.join('') : null,
|
|
31530
|
+
content: this.contentBuffer.length ? this.contentBuffer.join('') : null,
|
|
31775
31531
|
status: this.status
|
|
31776
31532
|
};
|
|
31777
|
-
if (this.options.debug) {
|
|
31778
|
-
console.log('[StreamParser] 刷新缓冲区:', {
|
|
31779
|
-
thinking: result.thinking?.length || 0,
|
|
31780
|
-
content: result.content?.length || 0,
|
|
31781
|
-
status: result.status,
|
|
31782
|
-
thinkingPreview: result.thinking?.substring(0, 50),
|
|
31783
|
-
contentPreview: result.content?.substring(0, 50)
|
|
31784
|
-
});
|
|
31785
|
-
}
|
|
31786
|
-
|
|
31787
|
-
// 清空缓冲区
|
|
31788
31533
|
this.thinkingBuffer = [];
|
|
31789
31534
|
this.contentBuffer = [];
|
|
31790
|
-
|
|
31791
|
-
// 回调更新
|
|
31792
|
-
try {
|
|
31793
|
-
callback(result);
|
|
31794
|
-
} catch (error) {
|
|
31795
|
-
console.error('[StreamParser] 回调执行错误:', error);
|
|
31796
|
-
}
|
|
31535
|
+
callback(result);
|
|
31797
31536
|
}
|
|
31798
31537
|
|
|
31799
|
-
|
|
31800
|
-
* 完成解析
|
|
31801
|
-
*/
|
|
31538
|
+
// 完成解析
|
|
31802
31539
|
finish(callback) {
|
|
31803
|
-
this.
|
|
31804
|
-
|
|
31805
|
-
|
|
31806
|
-
this.
|
|
31807
|
-
}
|
|
31808
|
-
if (this.options.debug) {
|
|
31809
|
-
const duration = Date.now() - this.metrics.startTime;
|
|
31810
|
-
console.log('[StreamParser] 解析完成:', {
|
|
31811
|
-
耗时: `${duration}ms`,
|
|
31812
|
-
数据块: this.metrics.chunks,
|
|
31813
|
-
事件数: this.metrics.events,
|
|
31814
|
-
字符数: this.metrics.chars,
|
|
31815
|
-
平均速度: `${(this.metrics.chars / duration * 1000).toFixed(0)} chars/s`
|
|
31816
|
-
});
|
|
31540
|
+
if (this.inTag && this.tagBuffer) {
|
|
31541
|
+
this.appendText(this.tagBuffer);
|
|
31542
|
+
this.tagBuffer = '';
|
|
31543
|
+
this.inTag = false;
|
|
31817
31544
|
}
|
|
31545
|
+
this.flush(callback);
|
|
31546
|
+
if (this.updateTimer) clearTimeout(this.updateTimer);
|
|
31818
31547
|
}
|
|
31819
31548
|
|
|
31820
|
-
|
|
31821
|
-
* 销毁解析器
|
|
31822
|
-
*/
|
|
31549
|
+
// 销毁解析器
|
|
31823
31550
|
destroy() {
|
|
31824
|
-
if (this.updateTimer)
|
|
31825
|
-
clearTimeout(this.updateTimer);
|
|
31826
|
-
}
|
|
31551
|
+
if (this.updateTimer) clearTimeout(this.updateTimer);
|
|
31827
31552
|
this.reset();
|
|
31828
31553
|
}
|
|
31829
31554
|
}
|
|
@@ -31905,7 +31630,7 @@ const getCookie = cname => {
|
|
|
31905
31630
|
this.streamParser.reset();
|
|
31906
31631
|
try {
|
|
31907
31632
|
const startTime = Date.now();
|
|
31908
|
-
const token = getCookie('bonyear-access_token') || `
|
|
31633
|
+
const token = getCookie('bonyear-access_token') || `44e7f112-63f3-429d-908d-2c97ec380de2`;
|
|
31909
31634
|
const response = await fetch(API_URL, {
|
|
31910
31635
|
method: 'POST',
|
|
31911
31636
|
headers: {
|
|
@@ -31914,7 +31639,7 @@ const getCookie = cname => {
|
|
|
31914
31639
|
},
|
|
31915
31640
|
body: JSON.stringify({
|
|
31916
31641
|
content: message,
|
|
31917
|
-
|
|
31642
|
+
chat_id: this.chatId
|
|
31918
31643
|
})
|
|
31919
31644
|
});
|
|
31920
31645
|
if (!response.ok) {
|
|
@@ -31968,9 +31693,10 @@ const getCookie = cname => {
|
|
|
31968
31693
|
const chunk = decoder.decode(value, {
|
|
31969
31694
|
stream: true
|
|
31970
31695
|
});
|
|
31971
|
-
|
|
31696
|
+
console.log('收到数据块:', chunk);
|
|
31972
31697
|
// 使用解析器处理数据块,确保this指向正确
|
|
31973
31698
|
this.streamParser.processChunk(chunk, function (result) {
|
|
31699
|
+
console.log('处理数据块:', result);
|
|
31974
31700
|
self.handleStreamUpdate(result);
|
|
31975
31701
|
});
|
|
31976
31702
|
}
|
|
@@ -31993,6 +31719,7 @@ const getCookie = cname => {
|
|
|
31993
31719
|
}
|
|
31994
31720
|
|
|
31995
31721
|
// 更新回复内容
|
|
31722
|
+
console.log('更新回复内容:', result.content);
|
|
31996
31723
|
if (result.content) {
|
|
31997
31724
|
this.currentMessage.content += result.content;
|
|
31998
31725
|
}
|
|
@@ -32127,6 +31854,7 @@ const startTime = null;
|
|
|
32127
31854
|
},
|
|
32128
31855
|
methods: {
|
|
32129
31856
|
toggleWindow() {
|
|
31857
|
+
if (this.avaterStatus === 'thinking') return;
|
|
32130
31858
|
this.visible = !this.visible;
|
|
32131
31859
|
if (this.visible) {
|
|
32132
31860
|
this.currentX = this.initialX;
|
|
@@ -32247,10 +31975,10 @@ const startTime = null;
|
|
|
32247
31975
|
});
|
|
32248
31976
|
;// ./components/ChatWindow.vue?vue&type=script&lang=js
|
|
32249
31977
|
/* harmony default export */ var components_ChatWindowvue_type_script_lang_js = (ChatWindowvue_type_script_lang_js);
|
|
32250
|
-
;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-54.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-54.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-54.use[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/ChatWindow.vue?vue&type=style&index=0&id=
|
|
31978
|
+
;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-54.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-54.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-54.use[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/ChatWindow.vue?vue&type=style&index=0&id=244aa1c1&prod&scoped=true&lang=css
|
|
32251
31979
|
// extracted by mini-css-extract-plugin
|
|
32252
31980
|
|
|
32253
|
-
;// ./components/ChatWindow.vue?vue&type=style&index=0&id=
|
|
31981
|
+
;// ./components/ChatWindow.vue?vue&type=style&index=0&id=244aa1c1&prod&scoped=true&lang=css
|
|
32254
31982
|
|
|
32255
31983
|
;// ./components/ChatWindow.vue
|
|
32256
31984
|
|
|
@@ -32267,7 +31995,7 @@ var ChatWindow_component = normalizeComponent(
|
|
|
32267
31995
|
staticRenderFns,
|
|
32268
31996
|
false,
|
|
32269
31997
|
null,
|
|
32270
|
-
"
|
|
31998
|
+
"244aa1c1",
|
|
32271
31999
|
null
|
|
32272
32000
|
|
|
32273
32001
|
)
|