byt-lingxiao-ai 0.3.5 → 0.3.8

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.
@@ -30,129 +30,6 @@ marked.setOptions({
30
30
  breaks: true,
31
31
  gfm: true
32
32
  });
33
- function parseMarkdown(text) {
34
- if (!text) return '';
35
- let html = text;
36
-
37
- html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
38
- return `<pre><code class="language-${lang || 'text'}">${escapeHtml(code.trim())}</code></pre>`;
39
- });
40
- html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
41
- html = parseTable(html);
42
-
43
- html = html.replace(/^#### (.*$)/gm, '<h4>$1</h4>');
44
- html = html.replace(/^### (.*$)/gm, '<h3>$1</h3>');
45
- html = html.replace(/^## (.*$)/gm, '<h2>$1</h2>');
46
- html = html.replace(/^# (.*$)/gm, '<h1>$1</h1>');
47
-
48
- html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
49
-
50
- html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');
51
-
52
- html = html.replace(/~~(.*?)~~/g, '<del>$1</del>');
53
-
54
- html = html.replace(/^\s*[-*+]\s+(.+)$/gm, '<li>$1</li>');
55
- html = html.replace(/(<li>.*?<\/li>)/gs, '<ul>$1</ul>');
56
-
57
- html = html.replace(/^\s*\d+\.\s+(.+)$/gm, '<li>$1</li>');
58
-
59
- html = html.replace(/^>\s+(.+)$/gm, '<blockquote>$1</blockquote>');
60
- // 解析Markdown链接
61
- html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" aria-current="page">$1</a>');
62
-
63
- html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" />');
64
-
65
- html = html.replace(/^-{3,}$/gm, '<hr>');
66
- // 解析简单URL
67
- // const simpleUrlRegex = /(https?:\/\/[^\s<>"']+)/g;
68
- // html = html.replace(simpleUrlRegex, (match) => {
69
- // return `<a href="${match}" aria-current="page">${match}</a>`;
70
- // });
71
-
72
- // html = html.replace(/\n/g, '<br>');
73
-
74
- return html;
75
- }
76
- // 解析表格
77
- function parseTable(text) {
78
- const lines = text.split('\n');
79
- let result = [];
80
- let inTable = false;
81
- let tableRows = [];
82
-
83
- for (let i = 0; i < lines.length; i++) {
84
- const line = lines[i].trim();
85
-
86
- // 检测表格行
87
- if (line.includes('|') && line.split('|').length >= 3) {
88
- if (!inTable) {
89
- inTable = true;
90
- tableRows = [];
91
- }
92
- tableRows.push(line);
93
-
94
- // 检查下一行是否还是表格
95
- if (i === lines.length - 1 || !lines[i + 1].includes('|')) {
96
- // 表格结束
97
- result.push(renderTable(tableRows));
98
- inTable = false;
99
- tableRows = [];
100
- }
101
- } else {
102
- if (inTable) {
103
- // 表格意外结束
104
- result.push(renderTable(tableRows));
105
- inTable = false;
106
- tableRows = [];
107
- }
108
- result.push(line);
109
- }
110
- }
111
-
112
- return result.join('\n');
113
- }
114
- // 渲染表格
115
- function renderTable(rows) {
116
- if (rows.length < 2) return rows.join('\n');
117
-
118
- let html = '<div class="table-wrapper"><table class="markdown-table">';
119
-
120
- // 表头
121
- const headerCells = rows[0].split('|').filter(cell => cell.trim());
122
- html += '<thead><tr>';
123
- headerCells.forEach(cell => {
124
- html += `<th>${cell.trim()}</th>`;
125
- });
126
- html += '</tr></thead>';
127
-
128
- // 表体(跳过分隔行)
129
- html += '<tbody>';
130
- for (let i = 2; i < rows.length; i++) {
131
- const cells = rows[i].split('|').filter(cell => cell.trim());
132
- if (cells.length > 0) {
133
- html += '<tr>';
134
- cells.forEach(cell => {
135
- html += `<td><div class="table-cell">${cell.trim()}</div></td>`;
136
- });
137
- html += '</tr>';
138
- }
139
- }
140
- html += '</tbody>';
141
-
142
- html += '</table></div>';
143
- return html;
144
- }
145
- // HTML 转义
146
- function escapeHtml(text) {
147
- const map = {
148
- '&': '&amp;',
149
- '<': '&lt;',
150
- '>': '&gt;',
151
- '"': '&quot;',
152
- "'": '&#039;'
153
- };
154
- return text.replace(/[&<>"']/g, m => map[m]);
155
- }
156
33
 
157
34
  export default {
158
35
  name: 'AiMessage',
@@ -167,7 +44,7 @@ export default {
167
44
  return this.message.thinkingExpanded !== false;
168
45
  },
169
46
  renderedContent() {
170
- return parseMarkdown(this.message.content);
47
+ return marked.parse(this.message.content || '');
171
48
  },
172
49
  isLoading() {
173
50
  return this.message.loading === true;
@@ -335,6 +212,19 @@ export default {
335
212
  .markdown-body ::v-deep h4 {
336
213
  font-size: 16px;
337
214
  }
215
+ .markdown-body ::v-deep ol {
216
+ list-style-type: decimal; /* 确保显示数字 */
217
+ padding-left: 24px;
218
+ margin: 8px 0;
219
+ }
220
+ .markdown-body ::v-deep ul {
221
+ list-style-type: disc; /* 确保显示圆点 */
222
+ padding-left: 24px;
223
+ margin: 8px 0;
224
+ }
225
+ .markdown-body ::v-deep li {
226
+ display: list-item; /* 确保是列表项显示 */
227
+ }
338
228
 
339
229
  .markdown-body ::v-deep code {
340
230
  background-color: rgba(175, 184, 193, 0.2);
@@ -367,52 +257,44 @@ export default {
367
257
  color: #24292e;
368
258
  font-size: 14px;
369
259
  }
370
- .markdown-body ::v-deep .table-wrapper{
371
- overflow-x: auto;
260
+ .markdown-body ::v-deep table{
372
261
  border: 1px solid #dfe2e5;
262
+ max-height: 500px;
373
263
  margin: 12px 0;
374
- }
375
- .markdown-body ::v-deep .markdown-table {
376
264
  border-collapse: collapse;
377
265
  width: 100%;
378
- font-size: 14px;
379
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
380
- overflow: hidden;
381
- margin: -1px 0 0 -1px;
266
+ display: block;
267
+ overflow: auto;
382
268
  }
383
269
  /* 定义table的滚动条 */
384
- .markdown-body ::v-deep .table-wrapper::-webkit-scrollbar {
270
+ .markdown-body ::v-deep table::-webkit-scrollbar {
385
271
  height: 6px;
386
272
  }
387
- .markdown-body ::v-deep .table-wrapper::-webkit-scrollbar-track {
273
+ .markdown-body ::v-deep table::-webkit-scrollbar-track {
388
274
  background-color: #f3f4f6;
389
275
  }
390
- .markdown-body ::v-deep .table-wrapper::-webkit-scrollbar-thumb {
276
+ .markdown-body ::v-deep table::-webkit-scrollbar-thumb {
391
277
  background-color: #d1d5db;
392
278
  border-radius: 3px;
393
279
  }
394
- .markdown-body ::v-deep .table-wrapper::-webkit-scrollbar-thumb:hover {
280
+ .markdown-body ::v-deep table::-webkit-scrollbar-thumb:hover {
395
281
  background-color: #9ca3af;
396
282
  }
397
283
 
398
- .markdown-body ::v-deep .table-cell {
399
- min-width: 100px;
400
- text-align: left;
401
- }
402
- .markdown-body ::v-deep .markdown-table th,
403
- .markdown-body ::v-deep .markdown-table td {
284
+ .markdown-body ::v-deep table th,
285
+ .markdown-body ::v-deep table td {
404
286
  border: 1px solid #dfe2e5;
405
287
  padding: 10px 14px;
406
288
  text-align: left;
407
289
  }
408
290
 
409
- .markdown-body ::v-deep .markdown-table th {
291
+ .markdown-body ::v-deep table th {
410
292
  background-color: #f3f4f6;
411
293
  font-weight: 600;
412
294
  color: #374151;
413
295
  }
414
296
 
415
- .markdown-body ::v-deep .markdown-table tr:nth-child(even) {
297
+ .markdown-body ::v-deep table tr:nth-child(even) {
416
298
  background-color: #f9fafb;
417
299
  }
418
300
 
@@ -137,6 +137,7 @@ export default {
137
137
  },
138
138
  methods: {
139
139
  toggleWindow() {
140
+ if (this.avaterStatus === 'thinking') return;
140
141
  this.visible = !this.visible
141
142
 
142
143
  if (this.visible) {
@@ -47,7 +47,7 @@ export default {
47
47
  },
48
48
  methods: {
49
49
  handleOpen() {
50
- const baseUrl = window.location.protocol + '//' + window.location.hostname + ':3100/c/' + this.chatId;
50
+ const baseUrl = window.location.origin + '/chat/c/' + this.chatId;
51
51
  window.open(baseUrl, '_blank')
52
52
  }
53
53
  }
@@ -1,8 +1,6 @@
1
- const baseUrl = window.location.protocol + '//' + window.location.hostname;
2
- const chatPort = '3100';
1
+ const baseUrl = window.location.origin;
3
2
 
4
- console.log(baseUrl, chatPort);
5
- export const API_URL = `${baseUrl}:${chatPort}/lingxiao-byt/api/v1/mcp/ask`;
6
- export const WS_URL = 'ws://192.168.8.9:9999/ai_model/ws/voice-stream';
7
- export const AUDIO_URL = '/minio/lingxiaoai/byt.mp3';
8
- export const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json';
3
+ export const API_URL = `/chat/lingxiao-byt/api/v1/mcp/ask`; // 对话
4
+ export const WS_URL = `ws://${baseUrl}/audio/ws/voice-stream`; // 语音
5
+ export const AUDIO_URL = '/minio/lingxiaoai/byt.mp3'; // 导览
6
+ export const TIME_JUMP_POINTS_URL = '/minio/lingxiaoai/timeJumpPoints.json'; // 语音url跳转节点
@@ -6,9 +6,7 @@ export default {
6
6
  audioContext: null,
7
7
  microphone: null,
8
8
  processor: null,
9
- audioBuffer: new Float32Array(0),
10
- // 优化
11
- int16Buffer: null
9
+ audioBuffer: new Float32Array(0)
12
10
  }
13
11
  },
14
12
  methods: {
@@ -20,21 +18,15 @@ export default {
20
18
  audio: {
21
19
  sampleRate: this.SAMPLE_RATE,
22
20
  channelCount: 1,
23
- noiseSuppression: true,
24
- echoCancellation: true
21
+ noiseSuppression: false,
22
+ echoCancellation: false,
23
+ autoGainControl: false,
25
24
  }
26
25
  });
27
26
 
28
27
  this.audioContext = new AudioContext({ sampleRate: this.SAMPLE_RATE });
29
-
30
- // 优化 处理 AudioContext 挂起
31
- if (this.audioContext.state === 'suspended') {
32
- await this.audioContext.resume();
33
- }
34
-
35
28
  this.microphone = this.audioContext.createMediaStreamSource(stream);
36
29
  this.processor = this.audioContext.createScriptProcessor(this.FRAME_SIZE, 1, 1);
37
- this.int16Buffer = new Int16Array(this.FRAME_SIZE);
38
30
  this.processor.onaudioprocess = this.processAudio;
39
31
 
40
32
  this.microphone.connect(this.processor);
@@ -53,20 +45,19 @@ export default {
53
45
  if (!this.isRecording) return;
54
46
  const inputData = event.inputBuffer.getChannelData(0);
55
47
 
56
- if (this.ws && this.ws.readyState === this.ws.OPEN) {
57
- if (inputData.length !== this.int16Buffer.length) {
58
- this.int16Buffer = new Int16Array(inputData.length);
59
- }
48
+ const tempBuffer = new Float32Array(this.audioBuffer.length + inputData.length);
49
+ tempBuffer.set(this.audioBuffer, 0);
50
+ tempBuffer.set(inputData, this.audioBuffer.length);
51
+ this.audioBuffer = tempBuffer;
60
52
 
61
- for (let i = 0; i < inputData.length; i++) {
62
- let s = inputData[i];
63
- // 简单的 clamp
64
- s = s < -1 ? -1 : s > 1 ? 1 : s;
65
- this.int16Buffer[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
66
- }
53
+ while (this.audioBuffer.length >= this.FRAME_SIZE) {
54
+ const frame = this.audioBuffer.slice(0, this.FRAME_SIZE);
55
+ this.audioBuffer = this.audioBuffer.slice(this.FRAME_SIZE);
56
+ const pcmData = this.floatTo16BitPCM(frame);
67
57
 
68
- // 4. 发送数据
69
- this.ws.send(this.int16Buffer.buffer);
58
+ if (this.ws && this.ws.readyState === this.ws.OPEN) {
59
+ this.ws.send(pcmData);
60
+ }
70
61
  }
71
62
  },
72
63
 
@@ -98,6 +89,7 @@ export default {
98
89
  },
99
90
 
100
91
  pause() {
92
+ console.log('暂停播放');
101
93
  this.robotStatus = 'waiting';
102
94
  this.$refs.audioPlayer.pause();
103
95
  },
@@ -125,6 +117,7 @@ export default {
125
117
  this.pause();
126
118
  } else if (command === 'C6') {
127
119
  this.robotStatus = 'leaving';
120
+ this.avaterStatus = 'normal';
128
121
  this.stop();
129
122
  }
130
123
  }
@@ -64,7 +64,7 @@ export default {
64
64
 
65
65
  try {
66
66
  const startTime = Date.now();
67
- const token = getCookie('bonyear-access_token') || `e298f087-85bc-48c2-afb9-7c69ffc911aa`;
67
+ const token = getCookie('bonyear-access_token') || `44e7f112-63f3-429d-908d-2c97ec380de2`;
68
68
 
69
69
  const response = await fetch(API_URL, {
70
70
  method: 'POST',
@@ -72,7 +72,7 @@ export default {
72
72
  'Content-Type': 'application/json',
73
73
  'Authorization': `Bearer ${token}`,
74
74
  },
75
- body: JSON.stringify({ content: message, chatId: this.chatId })
75
+ body: JSON.stringify({ content: message, chat_id: this.chatId })
76
76
  });
77
77
 
78
78
  if (!response.ok) {
@@ -126,9 +126,10 @@ export default {
126
126
  if (done) break;
127
127
 
128
128
  const chunk = decoder.decode(value, { stream: true });
129
-
129
+ console.log('收到数据块:', chunk);
130
130
  // 使用解析器处理数据块,确保this指向正确
131
131
  this.streamParser.processChunk(chunk, function(result) {
132
+ console.log('处理数据块:', result);
132
133
  self.handleStreamUpdate(result);
133
134
  });
134
135
  }
@@ -154,6 +155,7 @@ export default {
154
155
  }
155
156
 
156
157
  // 更新回复内容
158
+ console.log('更新回复内容:', result.content);
157
159
  if (result.content) {
158
160
  this.currentMessage.content += result.content;
159
161
  }
@@ -75,6 +75,10 @@ export default {
75
75
  handleWebSocketMessage(data) {
76
76
  if (data.type === 'detection') {
77
77
  console.log('检测到唤醒词');
78
+ console.log('当前状态:', this.avaterStatus);
79
+ if (this.robotStatus === 'speaking') {
80
+ this.robotStatus = 'waiting';
81
+ }
78
82
  this.avaterStatus = 'normal';
79
83
 
80
84
  // 性能检测起点
@@ -97,6 +101,8 @@ export default {
97
101
  this.analyzeAudioCommand(data.category);
98
102
  } else {
99
103
  console.log('状态: 其他');
104
+ this.avaterStatus = 'normal';
105
+ this.robotStatus = 'leaving';
100
106
  }
101
107
  },
102
108