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.common.js
CHANGED
|
@@ -28450,7 +28450,7 @@ if (typeof window !== 'undefined') {
|
|
|
28450
28450
|
var es_iterator_constructor = __webpack_require__(8111);
|
|
28451
28451
|
// EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.for-each.js
|
|
28452
28452
|
var es_iterator_for_each = __webpack_require__(7588);
|
|
28453
|
-
;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-40.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=
|
|
28453
|
+
;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-40.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
|
|
28454
28454
|
var render = function render() {
|
|
28455
28455
|
var _vm = this,
|
|
28456
28456
|
_c = _vm._self._c;
|
|
@@ -29017,8 +29017,8 @@ var UserMessage_component = normalizeComponent(
|
|
|
29017
29017
|
)
|
|
29018
29018
|
|
|
29019
29019
|
/* harmony default export */ var UserMessage = (UserMessage_component.exports);
|
|
29020
|
-
;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-40.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=
|
|
29021
|
-
var
|
|
29020
|
+
;// ./node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib/index.js??clonedRuleSet-40.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
|
|
29021
|
+
var AiMessagevue_type_template_id_6cbccb01_scoped_true_render = function render() {
|
|
29022
29022
|
var _vm = this,
|
|
29023
29023
|
_c = _vm._self._c;
|
|
29024
29024
|
return _c('div', {
|
|
@@ -29051,10 +29051,8 @@ var AiMessagevue_type_template_id_2422d7c0_scoped_true_render = function render(
|
|
|
29051
29051
|
}
|
|
29052
29052
|
})])]);
|
|
29053
29053
|
};
|
|
29054
|
-
var
|
|
29054
|
+
var AiMessagevue_type_template_id_6cbccb01_scoped_true_staticRenderFns = [];
|
|
29055
29055
|
|
|
29056
|
-
// EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.filter.js
|
|
29057
|
-
var es_iterator_filter = __webpack_require__(2489);
|
|
29058
29056
|
;// ./node_modules/@babel/runtime/helpers/esm/typeof.js
|
|
29059
29057
|
function _typeof(o) {
|
|
29060
29058
|
"@babel/helpers - typeof";
|
|
@@ -29098,6 +29096,8 @@ function _defineProperty(e, r, t) {
|
|
|
29098
29096
|
}) : e[r] = t, e;
|
|
29099
29097
|
}
|
|
29100
29098
|
|
|
29099
|
+
// EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.filter.js
|
|
29100
|
+
var es_iterator_filter = __webpack_require__(2489);
|
|
29101
29101
|
// EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.map.js
|
|
29102
29102
|
var es_iterator_map = __webpack_require__(1701);
|
|
29103
29103
|
// EXTERNAL MODULE: ./node_modules/core-js/modules/es.iterator.some.js
|
|
@@ -30827,10 +30827,6 @@ var lib = __webpack_require__(2750);
|
|
|
30827
30827
|
|
|
30828
30828
|
|
|
30829
30829
|
|
|
30830
|
-
|
|
30831
|
-
|
|
30832
|
-
|
|
30833
|
-
|
|
30834
30830
|
d.setOptions({
|
|
30835
30831
|
highlight: function (code, lang) {
|
|
30836
30832
|
if (lang && es.getLanguage(lang)) {
|
|
@@ -30843,115 +30839,6 @@ d.setOptions({
|
|
|
30843
30839
|
breaks: true,
|
|
30844
30840
|
gfm: true
|
|
30845
30841
|
});
|
|
30846
|
-
function parseMarkdown(text) {
|
|
30847
|
-
if (!text) return '';
|
|
30848
|
-
let html = text;
|
|
30849
|
-
html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
|
|
30850
|
-
return `<pre><code class="language-${lang || 'text'}">${escapeHtml(code.trim())}</code></pre>`;
|
|
30851
|
-
});
|
|
30852
|
-
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
30853
|
-
html = parseTable(html);
|
|
30854
|
-
html = html.replace(/^#### (.*$)/gm, '<h4>$1</h4>');
|
|
30855
|
-
html = html.replace(/^### (.*$)/gm, '<h3>$1</h3>');
|
|
30856
|
-
html = html.replace(/^## (.*$)/gm, '<h2>$1</h2>');
|
|
30857
|
-
html = html.replace(/^# (.*$)/gm, '<h1>$1</h1>');
|
|
30858
|
-
html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
|
|
30859
|
-
html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');
|
|
30860
|
-
html = html.replace(/~~(.*?)~~/g, '<del>$1</del>');
|
|
30861
|
-
html = html.replace(/^\s*[-*+]\s+(.+)$/gm, '<li>$1</li>');
|
|
30862
|
-
html = html.replace(/(<li>.*?<\/li>)/gs, '<ul>$1</ul>');
|
|
30863
|
-
html = html.replace(/^\s*\d+\.\s+(.+)$/gm, '<li>$1</li>');
|
|
30864
|
-
html = html.replace(/^>\s+(.+)$/gm, '<blockquote>$1</blockquote>');
|
|
30865
|
-
// 解析Markdown链接
|
|
30866
|
-
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" aria-current="page">$1</a>');
|
|
30867
|
-
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" />');
|
|
30868
|
-
html = html.replace(/^-{3,}$/gm, '<hr>');
|
|
30869
|
-
// 解析简单URL
|
|
30870
|
-
// const simpleUrlRegex = /(https?:\/\/[^\s<>"']+)/g;
|
|
30871
|
-
// html = html.replace(simpleUrlRegex, (match) => {
|
|
30872
|
-
// return `<a href="${match}" aria-current="page">${match}</a>`;
|
|
30873
|
-
// });
|
|
30874
|
-
|
|
30875
|
-
// html = html.replace(/\n/g, '<br>');
|
|
30876
|
-
|
|
30877
|
-
return html;
|
|
30878
|
-
}
|
|
30879
|
-
// 解析表格
|
|
30880
|
-
function parseTable(text) {
|
|
30881
|
-
const lines = text.split('\n');
|
|
30882
|
-
let result = [];
|
|
30883
|
-
let inTable = false;
|
|
30884
|
-
let tableRows = [];
|
|
30885
|
-
for (let i = 0; i < lines.length; i++) {
|
|
30886
|
-
const line = lines[i].trim();
|
|
30887
|
-
|
|
30888
|
-
// 检测表格行
|
|
30889
|
-
if (line.includes('|') && line.split('|').length >= 3) {
|
|
30890
|
-
if (!inTable) {
|
|
30891
|
-
inTable = true;
|
|
30892
|
-
tableRows = [];
|
|
30893
|
-
}
|
|
30894
|
-
tableRows.push(line);
|
|
30895
|
-
|
|
30896
|
-
// 检查下一行是否还是表格
|
|
30897
|
-
if (i === lines.length - 1 || !lines[i + 1].includes('|')) {
|
|
30898
|
-
// 表格结束
|
|
30899
|
-
result.push(renderTable(tableRows));
|
|
30900
|
-
inTable = false;
|
|
30901
|
-
tableRows = [];
|
|
30902
|
-
}
|
|
30903
|
-
} else {
|
|
30904
|
-
if (inTable) {
|
|
30905
|
-
// 表格意外结束
|
|
30906
|
-
result.push(renderTable(tableRows));
|
|
30907
|
-
inTable = false;
|
|
30908
|
-
tableRows = [];
|
|
30909
|
-
}
|
|
30910
|
-
result.push(line);
|
|
30911
|
-
}
|
|
30912
|
-
}
|
|
30913
|
-
return result.join('\n');
|
|
30914
|
-
}
|
|
30915
|
-
// 渲染表格
|
|
30916
|
-
function renderTable(rows) {
|
|
30917
|
-
if (rows.length < 2) return rows.join('\n');
|
|
30918
|
-
let html = '<div class="table-wrapper"><table class="markdown-table">';
|
|
30919
|
-
|
|
30920
|
-
// 表头
|
|
30921
|
-
const headerCells = rows[0].split('|').filter(cell => cell.trim());
|
|
30922
|
-
html += '<thead><tr>';
|
|
30923
|
-
headerCells.forEach(cell => {
|
|
30924
|
-
html += `<th>${cell.trim()}</th>`;
|
|
30925
|
-
});
|
|
30926
|
-
html += '</tr></thead>';
|
|
30927
|
-
|
|
30928
|
-
// 表体(跳过分隔行)
|
|
30929
|
-
html += '<tbody>';
|
|
30930
|
-
for (let i = 2; i < rows.length; i++) {
|
|
30931
|
-
const cells = rows[i].split('|').filter(cell => cell.trim());
|
|
30932
|
-
if (cells.length > 0) {
|
|
30933
|
-
html += '<tr>';
|
|
30934
|
-
cells.forEach(cell => {
|
|
30935
|
-
html += `<td><div class="table-cell">${cell.trim()}</div></td>`;
|
|
30936
|
-
});
|
|
30937
|
-
html += '</tr>';
|
|
30938
|
-
}
|
|
30939
|
-
}
|
|
30940
|
-
html += '</tbody>';
|
|
30941
|
-
html += '</table></div>';
|
|
30942
|
-
return html;
|
|
30943
|
-
}
|
|
30944
|
-
// HTML 转义
|
|
30945
|
-
function escapeHtml(text) {
|
|
30946
|
-
const map = {
|
|
30947
|
-
'&': '&',
|
|
30948
|
-
'<': '<',
|
|
30949
|
-
'>': '>',
|
|
30950
|
-
'"': '"',
|
|
30951
|
-
"'": '''
|
|
30952
|
-
};
|
|
30953
|
-
return text.replace(/[&<>"']/g, m => map[m]);
|
|
30954
|
-
}
|
|
30955
30842
|
/* harmony default export */ var AiMessagevue_type_script_lang_js = ({
|
|
30956
30843
|
name: 'AiMessage',
|
|
30957
30844
|
props: {
|
|
@@ -30965,7 +30852,7 @@ function escapeHtml(text) {
|
|
|
30965
30852
|
return this.message.thinkingExpanded !== false;
|
|
30966
30853
|
},
|
|
30967
30854
|
renderedContent() {
|
|
30968
|
-
return
|
|
30855
|
+
return d.parse(this.message.content || '');
|
|
30969
30856
|
},
|
|
30970
30857
|
isLoading() {
|
|
30971
30858
|
return this.message.loading === true;
|
|
@@ -30979,10 +30866,10 @@ function escapeHtml(text) {
|
|
|
30979
30866
|
});
|
|
30980
30867
|
;// ./components/AiMessage.vue?vue&type=script&lang=js
|
|
30981
30868
|
/* harmony default export */ var components_AiMessagevue_type_script_lang_js = (AiMessagevue_type_script_lang_js);
|
|
30982
|
-
;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-12.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-12.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-12.use[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/AiMessage.vue?vue&type=style&index=0&id=
|
|
30869
|
+
;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-12.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-12.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-12.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
|
|
30983
30870
|
// extracted by mini-css-extract-plugin
|
|
30984
30871
|
|
|
30985
|
-
;// ./components/AiMessage.vue?vue&type=style&index=0&id=
|
|
30872
|
+
;// ./components/AiMessage.vue?vue&type=style&index=0&id=6cbccb01&prod&scoped=true&lang=css
|
|
30986
30873
|
|
|
30987
30874
|
;// ./components/AiMessage.vue
|
|
30988
30875
|
|
|
@@ -30995,11 +30882,11 @@ function escapeHtml(text) {
|
|
|
30995
30882
|
|
|
30996
30883
|
var AiMessage_component = normalizeComponent(
|
|
30997
30884
|
components_AiMessagevue_type_script_lang_js,
|
|
30998
|
-
|
|
30999
|
-
|
|
30885
|
+
AiMessagevue_type_template_id_6cbccb01_scoped_true_render,
|
|
30886
|
+
AiMessagevue_type_template_id_6cbccb01_scoped_true_staticRenderFns,
|
|
31000
30887
|
false,
|
|
31001
30888
|
null,
|
|
31002
|
-
"
|
|
30889
|
+
"6cbccb01",
|
|
31003
30890
|
null
|
|
31004
30891
|
|
|
31005
30892
|
)
|
|
@@ -31297,9 +31184,7 @@ var es_typed_array_with = __webpack_require__(9577);
|
|
|
31297
31184
|
audioContext: null,
|
|
31298
31185
|
microphone: null,
|
|
31299
31186
|
processor: null,
|
|
31300
|
-
audioBuffer: new Float32Array(0)
|
|
31301
|
-
// 优化
|
|
31302
|
-
int16Buffer: null
|
|
31187
|
+
audioBuffer: new Float32Array(0)
|
|
31303
31188
|
};
|
|
31304
31189
|
},
|
|
31305
31190
|
methods: {
|
|
@@ -31311,21 +31196,16 @@ var es_typed_array_with = __webpack_require__(9577);
|
|
|
31311
31196
|
audio: {
|
|
31312
31197
|
sampleRate: this.SAMPLE_RATE,
|
|
31313
31198
|
channelCount: 1,
|
|
31314
|
-
noiseSuppression:
|
|
31315
|
-
echoCancellation:
|
|
31199
|
+
noiseSuppression: false,
|
|
31200
|
+
echoCancellation: false,
|
|
31201
|
+
autoGainControl: false
|
|
31316
31202
|
}
|
|
31317
31203
|
});
|
|
31318
31204
|
this.audioContext = new AudioContext({
|
|
31319
31205
|
sampleRate: this.SAMPLE_RATE
|
|
31320
31206
|
});
|
|
31321
|
-
|
|
31322
|
-
// 优化 处理 AudioContext 挂起
|
|
31323
|
-
if (this.audioContext.state === 'suspended') {
|
|
31324
|
-
await this.audioContext.resume();
|
|
31325
|
-
}
|
|
31326
31207
|
this.microphone = this.audioContext.createMediaStreamSource(stream);
|
|
31327
31208
|
this.processor = this.audioContext.createScriptProcessor(this.FRAME_SIZE, 1, 1);
|
|
31328
|
-
this.int16Buffer = new Int16Array(this.FRAME_SIZE);
|
|
31329
31209
|
this.processor.onaudioprocess = this.processAudio;
|
|
31330
31210
|
this.microphone.connect(this.processor);
|
|
31331
31211
|
this.processor.connect(this.audioContext.destination);
|
|
@@ -31340,19 +31220,17 @@ var es_typed_array_with = __webpack_require__(9577);
|
|
|
31340
31220
|
processAudio(event) {
|
|
31341
31221
|
if (!this.isRecording) return;
|
|
31342
31222
|
const inputData = event.inputBuffer.getChannelData(0);
|
|
31343
|
-
|
|
31344
|
-
|
|
31345
|
-
|
|
31223
|
+
const tempBuffer = new Float32Array(this.audioBuffer.length + inputData.length);
|
|
31224
|
+
tempBuffer.set(this.audioBuffer, 0);
|
|
31225
|
+
tempBuffer.set(inputData, this.audioBuffer.length);
|
|
31226
|
+
this.audioBuffer = tempBuffer;
|
|
31227
|
+
while (this.audioBuffer.length >= this.FRAME_SIZE) {
|
|
31228
|
+
const frame = this.audioBuffer.slice(0, this.FRAME_SIZE);
|
|
31229
|
+
this.audioBuffer = this.audioBuffer.slice(this.FRAME_SIZE);
|
|
31230
|
+
const pcmData = this.floatTo16BitPCM(frame);
|
|
31231
|
+
if (this.ws && this.ws.readyState === this.ws.OPEN) {
|
|
31232
|
+
this.ws.send(pcmData);
|
|
31346
31233
|
}
|
|
31347
|
-
for (let i = 0; i < inputData.length; i++) {
|
|
31348
|
-
let s = inputData[i];
|
|
31349
|
-
// 简单的 clamp
|
|
31350
|
-
s = s < -1 ? -1 : s > 1 ? 1 : s;
|
|
31351
|
-
this.int16Buffer[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
|
|
31352
|
-
}
|
|
31353
|
-
|
|
31354
|
-
// 4. 发送数据
|
|
31355
|
-
this.ws.send(this.int16Buffer.buffer);
|
|
31356
31234
|
}
|
|
31357
31235
|
},
|
|
31358
31236
|
floatTo16BitPCM(input) {
|
|
@@ -31380,6 +31258,7 @@ var es_typed_array_with = __webpack_require__(9577);
|
|
|
31380
31258
|
this.$refs.audioPlayer.play();
|
|
31381
31259
|
},
|
|
31382
31260
|
pause() {
|
|
31261
|
+
console.log('暂停播放');
|
|
31383
31262
|
this.robotStatus = 'waiting';
|
|
31384
31263
|
this.$refs.audioPlayer.pause();
|
|
31385
31264
|
},
|
|
@@ -31405,17 +31284,21 @@ var es_typed_array_with = __webpack_require__(9577);
|
|
|
31405
31284
|
this.pause();
|
|
31406
31285
|
} else if (command === 'C6') {
|
|
31407
31286
|
this.robotStatus = 'leaving';
|
|
31287
|
+
this.avaterStatus = 'normal';
|
|
31408
31288
|
this.stop();
|
|
31409
31289
|
}
|
|
31410
31290
|
}
|
|
31411
31291
|
}
|
|
31412
31292
|
});
|
|
31413
31293
|
;// ./components/config/index.js
|
|
31414
|
-
const
|
|
31294
|
+
const protocol = window.location.protocol;
|
|
31295
|
+
const host = window.location.hostname;
|
|
31296
|
+
const baseUrl = `${protocol}//${host}`;
|
|
31415
31297
|
const chatPort = '3100';
|
|
31298
|
+
const voicePort = '3101';
|
|
31416
31299
|
console.log(baseUrl, chatPort);
|
|
31417
31300
|
const API_URL = `${baseUrl}:${chatPort}/lingxiao-byt/api/v1/mcp/ask`;
|
|
31418
|
-
const WS_URL =
|
|
31301
|
+
const WS_URL = `ws://${host}:${voicePort}/ai_model/ws/voice-stream`;
|
|
31419
31302
|
const AUDIO_URL = '/minio/lingxiaoai/byt.mp3';
|
|
31420
31303
|
const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json';
|
|
31421
31304
|
;// ./components/mixins/webSocketMixin.js
|
|
@@ -31491,6 +31374,10 @@ const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json';
|
|
|
31491
31374
|
handleWebSocketMessage(data) {
|
|
31492
31375
|
if (data.type === 'detection') {
|
|
31493
31376
|
console.log('检测到唤醒词');
|
|
31377
|
+
console.log('当前状态:', this.avaterStatus);
|
|
31378
|
+
if (this.robotStatus === 'speaking') {
|
|
31379
|
+
this.robotStatus = 'waiting';
|
|
31380
|
+
}
|
|
31494
31381
|
this.avaterStatus = 'normal';
|
|
31495
31382
|
|
|
31496
31383
|
// 性能检测起点
|
|
@@ -31511,6 +31398,8 @@ const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json';
|
|
|
31511
31398
|
this.analyzeAudioCommand(data.category);
|
|
31512
31399
|
} else {
|
|
31513
31400
|
console.log('状态: 其他');
|
|
31401
|
+
this.avaterStatus = 'normal';
|
|
31402
|
+
this.robotStatus = 'leaving';
|
|
31514
31403
|
}
|
|
31515
31404
|
},
|
|
31516
31405
|
closeWebSocket() {
|
|
@@ -31522,222 +31411,99 @@ const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json';
|
|
|
31522
31411
|
});
|
|
31523
31412
|
;// ./components/utils/StreamParser.js
|
|
31524
31413
|
|
|
31525
|
-
|
|
31526
31414
|
class StreamParser {
|
|
31527
31415
|
constructor(options = {}) {
|
|
31528
31416
|
this.options = {
|
|
31529
31417
|
updateInterval: 16,
|
|
31530
|
-
//
|
|
31531
|
-
batchSize: 100,
|
|
31532
|
-
// 批处理大小
|
|
31418
|
+
// 每16ms刷新一次(约60fps)
|
|
31533
31419
|
debug: false,
|
|
31534
|
-
// 是否开启调试
|
|
31535
31420
|
...options
|
|
31536
31421
|
};
|
|
31537
31422
|
this.reset();
|
|
31538
31423
|
}
|
|
31539
31424
|
|
|
31540
|
-
|
|
31541
|
-
* 重置解析器状态
|
|
31542
|
-
*/
|
|
31425
|
+
// 重置解析器状态
|
|
31543
31426
|
reset() {
|
|
31544
31427
|
this.buffer = '';
|
|
31545
|
-
this.contentBuffer = [];
|
|
31428
|
+
this.contentBuffer = [];
|
|
31546
31429
|
this.thinkingBuffer = [];
|
|
31547
31430
|
this.inTag = false;
|
|
31548
31431
|
this.tagBuffer = '';
|
|
31549
31432
|
this.updateTimer = null;
|
|
31550
31433
|
this.status = 'output'; // thinking | output
|
|
31551
|
-
this.metrics = {
|
|
31552
|
-
startTime: Date.now(),
|
|
31553
|
-
chunks: 0,
|
|
31554
|
-
events: 0,
|
|
31555
|
-
chars: 0
|
|
31556
|
-
};
|
|
31557
31434
|
}
|
|
31558
31435
|
|
|
31559
|
-
|
|
31560
|
-
* 处理流数据块
|
|
31561
|
-
* @param {string} chunk - 接收到的数据块
|
|
31562
|
-
* @param {Function} callback - 更新回调函数
|
|
31563
|
-
*/
|
|
31436
|
+
// 处理接收到的流数据块
|
|
31564
31437
|
processChunk(chunk, callback) {
|
|
31565
|
-
|
|
31566
|
-
this.
|
|
31567
|
-
if (this.options.debug) {
|
|
31568
|
-
console.log('[StreamParser] 收到chunk:', chunk.substring(0, 100));
|
|
31569
|
-
}
|
|
31570
|
-
if (!this.buffer.includes('data:') && !this.buffer.includes('\n\n')) {
|
|
31571
|
-
// 纯文本流,直接处理
|
|
31572
|
-
if (this.options.debug) {
|
|
31573
|
-
console.log('[StreamParser] 检测到纯文本流');
|
|
31574
|
-
}
|
|
31575
|
-
this.processPlainText(callback);
|
|
31576
|
-
return;
|
|
31577
|
-
}
|
|
31578
|
-
|
|
31579
|
-
// 尝试解析为 SSE 格式
|
|
31580
|
-
if (this.buffer.includes('data:')) {
|
|
31581
|
-
this.processSSEFormat(callback);
|
|
31582
|
-
} else {
|
|
31583
|
-
// 直接处理纯文本流
|
|
31584
|
-
this.processPlainText(callback);
|
|
31585
|
-
}
|
|
31586
|
-
}
|
|
31587
|
-
|
|
31588
|
-
/**
|
|
31589
|
-
* 处理标准 SSE 格式
|
|
31590
|
-
*/
|
|
31591
|
-
processSSEFormat(callback) {
|
|
31592
|
-
// SSE 格式:事件由双换行符分隔
|
|
31593
|
-
const events = this.buffer.split('\n\n');
|
|
31594
|
-
|
|
31595
|
-
// 保留最后一个可能不完整的事件
|
|
31596
|
-
this.buffer = events.pop() || '';
|
|
31597
|
-
|
|
31598
|
-
// 处理完整的事件
|
|
31599
|
-
for (const event of events) {
|
|
31600
|
-
if (event.trim()) {
|
|
31601
|
-
this.metrics.events++;
|
|
31602
|
-
this.processSSEEvent(event, callback);
|
|
31603
|
-
}
|
|
31604
|
-
}
|
|
31438
|
+
if (!chunk) return;
|
|
31439
|
+
this.parseContent(chunk, callback);
|
|
31605
31440
|
}
|
|
31606
31441
|
|
|
31607
|
-
|
|
31608
|
-
* 处理纯文本流格式
|
|
31609
|
-
*/
|
|
31610
|
-
processPlainText(callback) {
|
|
31611
|
-
const content = this.buffer;
|
|
31612
|
-
this.buffer = ''; // 清空缓冲区
|
|
31613
|
-
|
|
31614
|
-
if (content) {
|
|
31615
|
-
this.metrics.chars += content.length;
|
|
31616
|
-
if (this.options.debug) {
|
|
31617
|
-
console.log('[StreamParser] 处理纯文本:', content);
|
|
31618
|
-
}
|
|
31619
|
-
this.parseContent(content, callback);
|
|
31620
|
-
}
|
|
31621
|
-
}
|
|
31622
|
-
|
|
31623
|
-
/**
|
|
31624
|
-
* 处理单个SSE事件
|
|
31625
|
-
*/
|
|
31626
|
-
processSSEEvent(eventStr, callback) {
|
|
31627
|
-
const lines = eventStr.split('\n');
|
|
31628
|
-
for (const line of lines) {
|
|
31629
|
-
// 解析 data: 行
|
|
31630
|
-
if (line.startsWith('data:')) {
|
|
31631
|
-
const data = line.substring(5).trim();
|
|
31632
|
-
if (data === '[DONE]') {
|
|
31633
|
-
this.flush(callback);
|
|
31634
|
-
return;
|
|
31635
|
-
}
|
|
31636
|
-
try {
|
|
31637
|
-
const parsed = JSON.parse(data);
|
|
31638
|
-
const content = parsed?.choices?.[0]?.delta?.content;
|
|
31639
|
-
if (content) {
|
|
31640
|
-
this.metrics.chars += content.length;
|
|
31641
|
-
if (this.options.debug) {
|
|
31642
|
-
console.log('[StreamParser] 解析SSE内容:', content);
|
|
31643
|
-
}
|
|
31644
|
-
this.parseContent(content, callback);
|
|
31645
|
-
}
|
|
31646
|
-
} catch (error) {
|
|
31647
|
-
if (this.options.debug) {
|
|
31648
|
-
console.warn('[StreamParser] JSON解析失败:', data, error);
|
|
31649
|
-
}
|
|
31650
|
-
}
|
|
31651
|
-
}
|
|
31652
|
-
}
|
|
31653
|
-
}
|
|
31654
|
-
|
|
31655
|
-
/**
|
|
31656
|
-
* 使用状态机解析内容和标签
|
|
31657
|
-
*/
|
|
31442
|
+
// 核心内容解析,支持 <think> 标签
|
|
31658
31443
|
parseContent(content, callback) {
|
|
31659
31444
|
let i = 0;
|
|
31660
31445
|
while (i < content.length) {
|
|
31661
31446
|
if (this.inTag) {
|
|
31662
|
-
// 在标签内部,查找标签结束
|
|
31663
31447
|
const endIndex = content.indexOf('>', i);
|
|
31664
31448
|
if (endIndex !== -1) {
|
|
31665
|
-
// 找到标签结束
|
|
31666
31449
|
this.tagBuffer += content.substring(i, endIndex + 1);
|
|
31667
31450
|
this.handleTag(this.tagBuffer);
|
|
31668
31451
|
this.tagBuffer = '';
|
|
31669
31452
|
this.inTag = false;
|
|
31670
31453
|
i = endIndex + 1;
|
|
31671
31454
|
} else {
|
|
31672
|
-
//
|
|
31455
|
+
// 标签未闭合,超过50字符强制输出,防止阻塞
|
|
31673
31456
|
this.tagBuffer += content.substring(i);
|
|
31457
|
+
if (this.tagBuffer.length > 50) {
|
|
31458
|
+
this.appendText(this.tagBuffer);
|
|
31459
|
+
this.tagBuffer = '';
|
|
31460
|
+
this.inTag = false;
|
|
31461
|
+
}
|
|
31674
31462
|
break;
|
|
31675
31463
|
}
|
|
31676
31464
|
} else {
|
|
31677
|
-
// 不在标签内,查找标签开始
|
|
31678
31465
|
const startIndex = content.indexOf('<', i);
|
|
31679
31466
|
if (startIndex !== -1) {
|
|
31680
|
-
|
|
31681
|
-
if (startIndex > i) {
|
|
31682
|
-
this.appendText(content.substring(i, startIndex));
|
|
31683
|
-
}
|
|
31684
|
-
|
|
31685
|
-
// 检查是否是完整标签
|
|
31467
|
+
if (startIndex > i) this.appendText(content.substring(i, startIndex));
|
|
31686
31468
|
const endIndex = content.indexOf('>', startIndex);
|
|
31687
31469
|
if (endIndex !== -1) {
|
|
31688
|
-
// 完整标签
|
|
31689
31470
|
const tag = content.substring(startIndex, endIndex + 1);
|
|
31690
31471
|
this.handleTag(tag);
|
|
31691
31472
|
i = endIndex + 1;
|
|
31692
31473
|
} else {
|
|
31693
|
-
|
|
31694
|
-
|
|
31695
|
-
|
|
31696
|
-
|
|
31474
|
+
const nextChar = content[startIndex + 1];
|
|
31475
|
+
if (!/[a-zA-Z/]/.test(nextChar)) {
|
|
31476
|
+
// 很可能不是标签,直接当文本输出
|
|
31477
|
+
this.appendText('<');
|
|
31478
|
+
i = startIndex + 1;
|
|
31479
|
+
} else {
|
|
31480
|
+
this.inTag = true;
|
|
31481
|
+
this.tagBuffer = content.substring(startIndex);
|
|
31482
|
+
break;
|
|
31483
|
+
}
|
|
31697
31484
|
}
|
|
31698
31485
|
} else {
|
|
31699
|
-
// 没有标签,全部是文本
|
|
31700
31486
|
this.appendText(content.substring(i));
|
|
31701
31487
|
break;
|
|
31702
31488
|
}
|
|
31703
31489
|
}
|
|
31704
31490
|
}
|
|
31705
|
-
|
|
31706
|
-
// 定时批量更新
|
|
31707
31491
|
this.scheduleUpdate(callback);
|
|
31708
31492
|
}
|
|
31709
31493
|
|
|
31710
|
-
|
|
31711
|
-
* 处理标签
|
|
31712
|
-
*/
|
|
31494
|
+
// 处理标签
|
|
31713
31495
|
handleTag(tag) {
|
|
31714
|
-
const
|
|
31715
|
-
if (this.
|
|
31716
|
-
console.log('[StreamParser] 处理标签:', tag);
|
|
31717
|
-
}
|
|
31718
|
-
if (tagName === '<think>') {
|
|
31719
|
-
this.status = 'thinking';
|
|
31720
|
-
} else if (tagName === '</think>') {
|
|
31721
|
-
this.status = 'output';
|
|
31722
|
-
}
|
|
31723
|
-
// 可扩展:支持更多标签类型
|
|
31496
|
+
const t = tag.toLowerCase();
|
|
31497
|
+
if (t === '<think>') this.status = 'thinking';else if (t === '</think>') this.status = 'output';
|
|
31724
31498
|
}
|
|
31725
31499
|
|
|
31726
|
-
|
|
31727
|
-
* 添加文本到缓冲区
|
|
31728
|
-
*/
|
|
31500
|
+
// 添加文本到缓冲区
|
|
31729
31501
|
appendText(text) {
|
|
31730
31502
|
if (!text) return;
|
|
31731
|
-
if (this.status === 'thinking')
|
|
31732
|
-
this.thinkingBuffer.push(text);
|
|
31733
|
-
} else {
|
|
31734
|
-
this.contentBuffer.push(text);
|
|
31735
|
-
}
|
|
31503
|
+
if (this.status === 'thinking') this.thinkingBuffer.push(text);else this.contentBuffer.push(text);
|
|
31736
31504
|
}
|
|
31737
31505
|
|
|
31738
|
-
|
|
31739
|
-
* 计划更新(防抖)
|
|
31740
|
-
*/
|
|
31506
|
+
// 防抖刷新
|
|
31741
31507
|
scheduleUpdate(callback) {
|
|
31742
31508
|
if (this.updateTimer) return;
|
|
31743
31509
|
this.updateTimer = setTimeout(() => {
|
|
@@ -31746,74 +31512,33 @@ class StreamParser {
|
|
|
31746
31512
|
}, this.options.updateInterval);
|
|
31747
31513
|
}
|
|
31748
31514
|
|
|
31749
|
-
|
|
31750
|
-
* 立即刷新缓冲区
|
|
31751
|
-
*/
|
|
31515
|
+
// 刷新缓冲区
|
|
31752
31516
|
flush(callback) {
|
|
31753
|
-
if (!callback)
|
|
31754
|
-
console.warn('[StreamParser] flush: callback 为空');
|
|
31755
|
-
return;
|
|
31756
|
-
}
|
|
31757
|
-
const hasThinking = this.thinkingBuffer.length > 0;
|
|
31758
|
-
const hasContent = this.contentBuffer.length > 0;
|
|
31759
|
-
if (!hasThinking && !hasContent) return;
|
|
31760
|
-
|
|
31761
|
-
// 使用 join 比字符串拼接性能更好
|
|
31517
|
+
if (!callback) return;
|
|
31762
31518
|
const result = {
|
|
31763
|
-
thinking:
|
|
31764
|
-
content:
|
|
31519
|
+
thinking: this.thinkingBuffer.length ? this.thinkingBuffer.join('') : null,
|
|
31520
|
+
content: this.contentBuffer.length ? this.contentBuffer.join('') : null,
|
|
31765
31521
|
status: this.status
|
|
31766
31522
|
};
|
|
31767
|
-
if (this.options.debug) {
|
|
31768
|
-
console.log('[StreamParser] 刷新缓冲区:', {
|
|
31769
|
-
thinking: result.thinking?.length || 0,
|
|
31770
|
-
content: result.content?.length || 0,
|
|
31771
|
-
status: result.status,
|
|
31772
|
-
thinkingPreview: result.thinking?.substring(0, 50),
|
|
31773
|
-
contentPreview: result.content?.substring(0, 50)
|
|
31774
|
-
});
|
|
31775
|
-
}
|
|
31776
|
-
|
|
31777
|
-
// 清空缓冲区
|
|
31778
31523
|
this.thinkingBuffer = [];
|
|
31779
31524
|
this.contentBuffer = [];
|
|
31780
|
-
|
|
31781
|
-
// 回调更新
|
|
31782
|
-
try {
|
|
31783
|
-
callback(result);
|
|
31784
|
-
} catch (error) {
|
|
31785
|
-
console.error('[StreamParser] 回调执行错误:', error);
|
|
31786
|
-
}
|
|
31525
|
+
callback(result);
|
|
31787
31526
|
}
|
|
31788
31527
|
|
|
31789
|
-
|
|
31790
|
-
* 完成解析
|
|
31791
|
-
*/
|
|
31528
|
+
// 完成解析
|
|
31792
31529
|
finish(callback) {
|
|
31793
|
-
this.
|
|
31794
|
-
|
|
31795
|
-
|
|
31796
|
-
this.
|
|
31797
|
-
}
|
|
31798
|
-
if (this.options.debug) {
|
|
31799
|
-
const duration = Date.now() - this.metrics.startTime;
|
|
31800
|
-
console.log('[StreamParser] 解析完成:', {
|
|
31801
|
-
耗时: `${duration}ms`,
|
|
31802
|
-
数据块: this.metrics.chunks,
|
|
31803
|
-
事件数: this.metrics.events,
|
|
31804
|
-
字符数: this.metrics.chars,
|
|
31805
|
-
平均速度: `${(this.metrics.chars / duration * 1000).toFixed(0)} chars/s`
|
|
31806
|
-
});
|
|
31530
|
+
if (this.inTag && this.tagBuffer) {
|
|
31531
|
+
this.appendText(this.tagBuffer);
|
|
31532
|
+
this.tagBuffer = '';
|
|
31533
|
+
this.inTag = false;
|
|
31807
31534
|
}
|
|
31535
|
+
this.flush(callback);
|
|
31536
|
+
if (this.updateTimer) clearTimeout(this.updateTimer);
|
|
31808
31537
|
}
|
|
31809
31538
|
|
|
31810
|
-
|
|
31811
|
-
* 销毁解析器
|
|
31812
|
-
*/
|
|
31539
|
+
// 销毁解析器
|
|
31813
31540
|
destroy() {
|
|
31814
|
-
if (this.updateTimer)
|
|
31815
|
-
clearTimeout(this.updateTimer);
|
|
31816
|
-
}
|
|
31541
|
+
if (this.updateTimer) clearTimeout(this.updateTimer);
|
|
31817
31542
|
this.reset();
|
|
31818
31543
|
}
|
|
31819
31544
|
}
|
|
@@ -31895,7 +31620,7 @@ const getCookie = cname => {
|
|
|
31895
31620
|
this.streamParser.reset();
|
|
31896
31621
|
try {
|
|
31897
31622
|
const startTime = Date.now();
|
|
31898
|
-
const token = getCookie('bonyear-access_token') || `
|
|
31623
|
+
const token = getCookie('bonyear-access_token') || `44e7f112-63f3-429d-908d-2c97ec380de2`;
|
|
31899
31624
|
const response = await fetch(API_URL, {
|
|
31900
31625
|
method: 'POST',
|
|
31901
31626
|
headers: {
|
|
@@ -31904,7 +31629,7 @@ const getCookie = cname => {
|
|
|
31904
31629
|
},
|
|
31905
31630
|
body: JSON.stringify({
|
|
31906
31631
|
content: message,
|
|
31907
|
-
|
|
31632
|
+
chat_id: this.chatId
|
|
31908
31633
|
})
|
|
31909
31634
|
});
|
|
31910
31635
|
if (!response.ok) {
|
|
@@ -31958,9 +31683,10 @@ const getCookie = cname => {
|
|
|
31958
31683
|
const chunk = decoder.decode(value, {
|
|
31959
31684
|
stream: true
|
|
31960
31685
|
});
|
|
31961
|
-
|
|
31686
|
+
console.log('收到数据块:', chunk);
|
|
31962
31687
|
// 使用解析器处理数据块,确保this指向正确
|
|
31963
31688
|
this.streamParser.processChunk(chunk, function (result) {
|
|
31689
|
+
console.log('处理数据块:', result);
|
|
31964
31690
|
self.handleStreamUpdate(result);
|
|
31965
31691
|
});
|
|
31966
31692
|
}
|
|
@@ -31983,6 +31709,7 @@ const getCookie = cname => {
|
|
|
31983
31709
|
}
|
|
31984
31710
|
|
|
31985
31711
|
// 更新回复内容
|
|
31712
|
+
console.log('更新回复内容:', result.content);
|
|
31986
31713
|
if (result.content) {
|
|
31987
31714
|
this.currentMessage.content += result.content;
|
|
31988
31715
|
}
|
|
@@ -32117,6 +31844,7 @@ const startTime = null;
|
|
|
32117
31844
|
},
|
|
32118
31845
|
methods: {
|
|
32119
31846
|
toggleWindow() {
|
|
31847
|
+
if (this.avaterStatus === 'thinking') return;
|
|
32120
31848
|
this.visible = !this.visible;
|
|
32121
31849
|
if (this.visible) {
|
|
32122
31850
|
this.currentX = this.initialX;
|
|
@@ -32237,10 +31965,10 @@ const startTime = null;
|
|
|
32237
31965
|
});
|
|
32238
31966
|
;// ./components/ChatWindow.vue?vue&type=script&lang=js
|
|
32239
31967
|
/* harmony default export */ var components_ChatWindowvue_type_script_lang_js = (ChatWindowvue_type_script_lang_js);
|
|
32240
|
-
;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-12.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-12.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-12.use[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./components/ChatWindow.vue?vue&type=style&index=0&id=
|
|
31968
|
+
;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-12.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-12.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-12.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
|
|
32241
31969
|
// extracted by mini-css-extract-plugin
|
|
32242
31970
|
|
|
32243
|
-
;// ./components/ChatWindow.vue?vue&type=style&index=0&id=
|
|
31971
|
+
;// ./components/ChatWindow.vue?vue&type=style&index=0&id=244aa1c1&prod&scoped=true&lang=css
|
|
32244
31972
|
|
|
32245
31973
|
;// ./components/ChatWindow.vue
|
|
32246
31974
|
|
|
@@ -32257,7 +31985,7 @@ var ChatWindow_component = normalizeComponent(
|
|
|
32257
31985
|
staticRenderFns,
|
|
32258
31986
|
false,
|
|
32259
31987
|
null,
|
|
32260
|
-
"
|
|
31988
|
+
"244aa1c1",
|
|
32261
31989
|
null
|
|
32262
31990
|
|
|
32263
31991
|
)
|