|
154 | 154 |
|
155 | 155 | let taskIds = null; |
156 | 156 |
|
| 157 | + // Streaming render state |
| 158 | + let renderRafId = null; |
| 159 | + let lastTtsProcessTime = 0; |
| 160 | +
|
157 | 161 | // Chat Input |
158 | 162 | let prompt = ''; |
159 | 163 | let chatFiles = []; |
|
382 | 386 | }; |
383 | 387 |
|
384 | 388 | const chatEventHandler = async (event, cb) => { |
385 | | - console.log(event); |
386 | | -
|
387 | 389 | if (event.chat_id === $chatId) { |
388 | 390 | await tick(); |
389 | 391 | let message = history.messages[event.message_id]; |
|
508 | 510 | console.log('Unknown message type', data); |
509 | 511 | } |
510 | 512 |
|
511 | | - history.messages[event.message_id] = message; |
| 513 | + // Batch reactivity updates to animation frame rate during streaming |
| 514 | + if (type === 'chat:completion' && !data?.done) { |
| 515 | + if (!renderRafId) { |
| 516 | + const msgId = event.message_id; |
| 517 | + renderRafId = requestAnimationFrame(() => { |
| 518 | + renderRafId = null; |
| 519 | + history.messages[msgId] = history.messages[msgId]; |
| 520 | + if (autoScroll) { |
| 521 | + scrollToBottom(); |
| 522 | + } |
| 523 | + }); |
| 524 | + } |
| 525 | + } else { |
| 526 | + history.messages[event.message_id] = message; |
| 527 | + } |
512 | 528 | } |
513 | 529 | } |
514 | 530 | }; |
|
1479 | 1495 | navigator.vibrate(5); |
1480 | 1496 | } |
1481 | 1497 |
|
1482 | | - // Emit chat event for TTS |
1483 | | - const messageContentParts = getMessageContentParts( |
1484 | | - removeAllDetails(message.content), |
1485 | | - $config?.audio?.tts?.split_on ?? 'punctuation' |
1486 | | - ); |
1487 | | - messageContentParts.pop(); |
| 1498 | + // Throttle TTS processing |
| 1499 | + const now = Date.now(); |
| 1500 | + if (now - lastTtsProcessTime >= 150) { |
| 1501 | + lastTtsProcessTime = now; |
1488 | 1502 |
|
1489 | | - // dispatch only last sentence and make sure it hasn't been dispatched before |
1490 | | - if ( |
1491 | | - messageContentParts.length > 0 && |
1492 | | - messageContentParts[messageContentParts.length - 1] !== message.lastSentence |
1493 | | - ) { |
1494 | | - message.lastSentence = messageContentParts[messageContentParts.length - 1]; |
1495 | | - eventTarget.dispatchEvent( |
1496 | | - new CustomEvent('chat', { |
1497 | | - detail: { |
1498 | | - id: message.id, |
1499 | | - content: messageContentParts[messageContentParts.length - 1] |
1500 | | - } |
1501 | | - }) |
| 1503 | + const messageContentParts = getMessageContentParts( |
| 1504 | + removeAllDetails(message.content), |
| 1505 | + $config?.audio?.tts?.split_on ?? 'punctuation' |
1502 | 1506 | ); |
| 1507 | + messageContentParts.pop(); |
| 1508 | +
|
| 1509 | + if ( |
| 1510 | + messageContentParts.length > 0 && |
| 1511 | + messageContentParts[messageContentParts.length - 1] !== message.lastSentence |
| 1512 | + ) { |
| 1513 | + message.lastSentence = messageContentParts[messageContentParts.length - 1]; |
| 1514 | + eventTarget.dispatchEvent( |
| 1515 | + new CustomEvent('chat', { |
| 1516 | + detail: { |
| 1517 | + id: message.id, |
| 1518 | + content: messageContentParts[messageContentParts.length - 1] |
| 1519 | + } |
| 1520 | + }) |
| 1521 | + ); |
| 1522 | + } |
1503 | 1523 | } |
1504 | 1524 | } |
1505 | 1525 | } |
|
1513 | 1533 | navigator.vibrate(5); |
1514 | 1534 | } |
1515 | 1535 |
|
1516 | | - // Emit chat event for TTS |
1517 | | - const messageContentParts = getMessageContentParts( |
1518 | | - removeAllDetails(message.content), |
1519 | | - $config?.audio?.tts?.split_on ?? 'punctuation' |
1520 | | - ); |
1521 | | - messageContentParts.pop(); |
| 1536 | + // Throttle TTS processing |
| 1537 | + const now = Date.now(); |
| 1538 | + if (now - lastTtsProcessTime >= 150) { |
| 1539 | + lastTtsProcessTime = now; |
1522 | 1540 |
|
1523 | | - // dispatch only last sentence and make sure it hasn't been dispatched before |
1524 | | - if ( |
1525 | | - messageContentParts.length > 0 && |
1526 | | - messageContentParts[messageContentParts.length - 1] !== message.lastSentence |
1527 | | - ) { |
1528 | | - message.lastSentence = messageContentParts[messageContentParts.length - 1]; |
1529 | | - eventTarget.dispatchEvent( |
1530 | | - new CustomEvent('chat', { |
1531 | | - detail: { |
1532 | | - id: message.id, |
1533 | | - content: messageContentParts[messageContentParts.length - 1] |
1534 | | - } |
1535 | | - }) |
| 1541 | + const messageContentParts = getMessageContentParts( |
| 1542 | + removeAllDetails(message.content), |
| 1543 | + $config?.audio?.tts?.split_on ?? 'punctuation' |
1536 | 1544 | ); |
| 1545 | + messageContentParts.pop(); |
| 1546 | +
|
| 1547 | + if ( |
| 1548 | + messageContentParts.length > 0 && |
| 1549 | + messageContentParts[messageContentParts.length - 1] !== message.lastSentence |
| 1550 | + ) { |
| 1551 | + message.lastSentence = messageContentParts[messageContentParts.length - 1]; |
| 1552 | + eventTarget.dispatchEvent( |
| 1553 | + new CustomEvent('chat', { |
| 1554 | + detail: { |
| 1555 | + id: message.id, |
| 1556 | + content: messageContentParts[messageContentParts.length - 1] |
| 1557 | + } |
| 1558 | + }) |
| 1559 | + ); |
| 1560 | + } |
1537 | 1561 | } |
1538 | 1562 | } |
1539 | 1563 |
|
|
1546 | 1570 | message.usage = usage; |
1547 | 1571 | } |
1548 | 1572 |
|
1549 | | - history.messages[message.id] = message; |
1550 | | -
|
1551 | 1573 | if (done) { |
1552 | 1574 | message.done = true; |
1553 | 1575 |
|
|
1596 | 1618 | createMessagesList(history, message.id) |
1597 | 1619 | ); |
1598 | 1620 | } |
1599 | | -
|
1600 | | - console.log(data); |
1601 | | - await tick(); |
1602 | | -
|
1603 | | - if (autoScroll) { |
1604 | | - scrollToBottom(); |
1605 | | - } |
1606 | 1621 | }; |
1607 | 1622 |
|
1608 | 1623 | ////////////////////////// |
|
0 commit comments