@mujian/js-sdk 0.0.6-beta.9 → 0.0.6-beta.mjv.67
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/dist/events/index.d.ts +30 -2
- package/dist/index.d.ts +13 -39
- package/dist/index.js +323 -15
- package/dist/modules/ai/chat/chat.d.ts +46 -4
- package/dist/modules/ai/chat/index.d.ts +1 -1
- package/dist/modules/ai/chat/message/index.d.ts +4 -0
- package/dist/modules/ai/index.d.ts +3 -2
- package/dist/modules/ai/openai/chat.d.ts +25 -0
- package/dist/modules/ai/openai/completions.d.ts +19 -0
- package/dist/modules/ai/openai/images.d.ts +19 -0
- package/dist/modules/ai/openai/index.d.ts +4 -0
- package/dist/modules/ai/openai/responses.d.ts +20 -0
- package/dist/modules/ai/text/index.d.ts +9 -2
- package/dist/modules/utils/clipboard.d.ts +5 -0
- package/dist/modules/utils/index.d.ts +4 -0
- package/dist/react/chat/useChat/index.d.ts +7 -3
- package/dist/react/chat/useChat/inner/chatStreaming.d.ts +1 -1
- package/dist/react/chat/useChat/message.d.ts +25 -13
- package/dist/react/components/MdRenderer/index.d.ts +4 -3
- package/dist/react/components/MdRenderer/utils/height.d.ts +0 -0
- package/dist/react/components/MdRenderer/utils/iframe.d.ts +9 -0
- package/dist/react/components/MdRenderer/utils/scripts.d.ts +4 -0
- package/dist/react/components/MujianSpinner/index.d.ts +7 -0
- package/dist/react/components/index.d.ts +1 -0
- package/dist/react.css +65 -4
- package/dist/react.js +687 -112
- package/dist/types/index.d.ts +38 -0
- package/dist/umd/index.js +792 -0
- package/dist/umd/index.js.LICENSE.txt +7 -0
- package/dist/umd/react.css +343 -0
- package/dist/umd/react.js +8881 -0
- package/dist/umd/react.js.LICENSE.txt +31 -0
- package/dist/utils/log.d.ts +4 -0
- package/package.json +4 -1
- /package/dist/react/components/MdRenderer/{utils.d.ts → utils/md.d.ts} +0 -0
package/dist/react.js
CHANGED
|
@@ -1,11 +1,181 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import react, { createContext, forwardRef, useCallback, useContext, useEffect, useState } from "react";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
1
|
+
import { useInfiniteScroll, useLatest, useRequest, useUpdateEffect } from "ahooks";
|
|
2
|
+
import react, { createContext, forwardRef, useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import css_tools from "@adobe/css-tools";
|
|
5
5
|
import dompurify from "dompurify";
|
|
6
6
|
import showdown from "showdown";
|
|
7
|
+
import { v4 } from "uuid";
|
|
7
8
|
import postmate from "postmate";
|
|
8
9
|
import { Virtualizer } from "virtua";
|
|
10
|
+
const adjustIframeHeight = (iframeId)=>`
|
|
11
|
+
(function () {
|
|
12
|
+
let scheduled = false;
|
|
13
|
+
function measureAndPost() {
|
|
14
|
+
scheduled = false;
|
|
15
|
+
try {
|
|
16
|
+
const doc = window.document;
|
|
17
|
+
const body = doc.body;
|
|
18
|
+
const html = doc.documentElement;
|
|
19
|
+
if (!body || !html) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let height = 0;
|
|
23
|
+
// srcdoc 模式: 只用 body.scrollHeight
|
|
24
|
+
height = body.scrollHeight;
|
|
25
|
+
if (!Number.isFinite(height) || height <= 0) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
window.parent.postMessage({ type: 'MJ_ADJUST_IFRAME_HEIGHT', iframe_id: \`${iframeId}\`, height: height }, '*');
|
|
29
|
+
} catch {
|
|
30
|
+
console.error('Error measuring iframe height');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function postIframeHeight() {
|
|
35
|
+
if (scheduled) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
scheduled = true;
|
|
39
|
+
if (typeof window.requestAnimationFrame === 'function') {
|
|
40
|
+
window.requestAnimationFrame(measureAndPost);
|
|
41
|
+
} else {
|
|
42
|
+
setTimeout(measureAndPost, 500);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function observeHeightChange() {
|
|
47
|
+
const body = document.body;
|
|
48
|
+
if (!body) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const observer = new ResizeObserver(entries => {
|
|
52
|
+
postIframeHeight();
|
|
53
|
+
});
|
|
54
|
+
observer.observe(body);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function init() {
|
|
58
|
+
postIframeHeight();
|
|
59
|
+
observeHeightChange();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (window.document.readyState === 'loading') {
|
|
63
|
+
window.document.addEventListener('DOMContentLoaded', init, { once: true });
|
|
64
|
+
} else {
|
|
65
|
+
init();
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
`;
|
|
69
|
+
const iframeAdjustViewport = (parentHeight)=>`
|
|
70
|
+
$('html').css('--MJ-viewport-height', \`${parentHeight}px\`);
|
|
71
|
+
window.addEventListener('message', function (event) {
|
|
72
|
+
if (event.data?.type === 'MJ_UPDATE_VIEWPORT_HEIGHT') {
|
|
73
|
+
$('html').css('--MJ-viewport-height', \`${parentHeight}px\`);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
`;
|
|
77
|
+
const init = (_iframeId, unsafe, extra)=>unsafe ? '' : `
|
|
78
|
+
(async function () {
|
|
79
|
+
const extra = JSON.parse(${JSON.stringify(JSON.stringify(extra))})
|
|
80
|
+
window.$mujian = window.parent.$mj_engine.bind(extra);
|
|
81
|
+
})();
|
|
82
|
+
`;
|
|
83
|
+
const thirdParty = `
|
|
84
|
+
<script src="https://cdn.jsdmirror.com/npm/@fortawesome/fontawesome-free/js/all.min.js"></script>
|
|
85
|
+
<script src="https://cdn.jsdmirror.com/npm/@tailwindcss/browser/dist/index.global.min.js"></script>
|
|
86
|
+
<script src="https://cdn.jsdmirror.com/npm/jquery/dist/jquery.min.js"></script>
|
|
87
|
+
<script src="https://cdn.jsdmirror.com/npm/jquery-ui/dist/jquery-ui.min.js"></script>
|
|
88
|
+
<link rel="stylesheet" href="https://cdn.jsdmirror.com/npm/jquery-ui/themes/base/theme.min.css" />
|
|
89
|
+
<script src="https://cdn.jsdmirror.com/npm/jquery-ui-touch-punch"></script>
|
|
90
|
+
`;
|
|
91
|
+
const unescapeHTML = (str)=>{
|
|
92
|
+
const named = {
|
|
93
|
+
amp: '&',
|
|
94
|
+
lt: '<',
|
|
95
|
+
gt: '>',
|
|
96
|
+
quot: '"',
|
|
97
|
+
apos: "'",
|
|
98
|
+
nbsp: '\u00A0'
|
|
99
|
+
};
|
|
100
|
+
return str.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (_m, body)=>{
|
|
101
|
+
if ('#' === body[0]) {
|
|
102
|
+
const isHex = body[1]?.toLowerCase() === 'x';
|
|
103
|
+
const numStr = isHex ? body.slice(2) : body.slice(1);
|
|
104
|
+
const codePoint = parseInt(numStr, isHex ? 16 : 10);
|
|
105
|
+
if (Number.isFinite(codePoint)) try {
|
|
106
|
+
return String.fromCodePoint(codePoint);
|
|
107
|
+
} catch {}
|
|
108
|
+
return _m;
|
|
109
|
+
}
|
|
110
|
+
const lower = body.toLowerCase();
|
|
111
|
+
return Object.hasOwn(named, lower) ? named[lower] : _m;
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
const replaceVhInContent = (content)=>{
|
|
115
|
+
const has_css_min_vh = /min-height\s*:\s*[^;{}]*\d+(?:\.\d+)?vh/gi.test(content);
|
|
116
|
+
const has_inline_style_vh = /style\s*=\s*(["'])[\s\S]*?min-height\s*:\s*[^;]*?\d+(?:\.\d+)?vh[\s\S]*?\1/gi.test(content);
|
|
117
|
+
const has_js_vh = /(\.style\.minHeight\s*=\s*(["']))([\s\S]*?vh)(\2)/gi.test(content) || /(setProperty\s*\(\s*(["'])min-height\2\s*,\s*(["']))([\s\S]*?vh)(\3\s*\))/gi.test(content);
|
|
118
|
+
if (!has_css_min_vh && !has_inline_style_vh && !has_js_vh) return content;
|
|
119
|
+
const convertVhToVariable = (value)=>value.replace(/(\d+(?:\.\d+)?)vh\b/gi, (match, value)=>{
|
|
120
|
+
const parsed = parseFloat(value);
|
|
121
|
+
if (!isFinite(parsed)) return match;
|
|
122
|
+
const VARIABLE_EXPRESSION = "var(--MJ-viewport-height)";
|
|
123
|
+
if (100 === parsed) return VARIABLE_EXPRESSION;
|
|
124
|
+
return `calc(${VARIABLE_EXPRESSION} * ${parsed / 100})`;
|
|
125
|
+
});
|
|
126
|
+
content = content.replace(/(min-height\s*:\s*)([^;{}]*?\d+(?:\.\d+)?vh)(?=\s*[;}])/gi, (_m, prefix, value)=>`${prefix}${convertVhToVariable(value)}`);
|
|
127
|
+
content = content.replace(/(style\s*=\s*(["']))([^"'"]*?)(\2)/gi, (match, prefix, _quote, styleContent, suffix)=>{
|
|
128
|
+
if (!/min-height\s*:\s*[^;]*vh/i.test(styleContent)) return match;
|
|
129
|
+
const replaced = styleContent.replace(/(min-height\s*:\s*)([^;]*?\d+(?:\.\d+)?vh)/gi, (_m, p1, p2)=>`${p1}${convertVhToVariable(p2)}`);
|
|
130
|
+
return `${prefix}${replaced}${suffix}`;
|
|
131
|
+
});
|
|
132
|
+
content = content.replace(/(\.style\.minHeight\s*=\s*(["']))([\s\S]*?)(\2)/gi, (match, prefix, _q, val, suffix)=>{
|
|
133
|
+
if (!/\b\d+(?:\.\d+)?vh\b/i.test(val)) return match;
|
|
134
|
+
const converted = convertVhToVariable(val);
|
|
135
|
+
return `${prefix}${converted}${suffix}`;
|
|
136
|
+
});
|
|
137
|
+
content = content.replace(/(setProperty\s*\(\s*(["'])min-height\2\s*,\s*(["']))([\s\S]*?)(\3\s*\))/gi, (match, prefix, _q1, _q2, val, suffix)=>{
|
|
138
|
+
if (!/\b\d+(?:\.\d+)?vh\b/i.test(val)) return match;
|
|
139
|
+
const converted = convertVhToVariable(val);
|
|
140
|
+
return `${prefix}${converted}${suffix}`;
|
|
141
|
+
});
|
|
142
|
+
return content;
|
|
143
|
+
};
|
|
144
|
+
function escapeHtmlAttribute(value) {
|
|
145
|
+
return value.replace(/"/g, '"').replace(/'/g, ''');
|
|
146
|
+
}
|
|
147
|
+
function createSrcContent(content, iframeId, unsafe, extra) {
|
|
148
|
+
content = replaceVhInContent(content);
|
|
149
|
+
return `
|
|
150
|
+
<html>
|
|
151
|
+
<head>
|
|
152
|
+
<meta charset="utf-8">
|
|
153
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
154
|
+
<style>
|
|
155
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
156
|
+
html,body{margin:0!important;padding:0;overflow:hidden!important;max-width:100%!important;}
|
|
157
|
+
</style>
|
|
158
|
+
${thirdParty}
|
|
159
|
+
<script>
|
|
160
|
+
${init(iframeId, unsafe, extra)}
|
|
161
|
+
${adjustIframeHeight(iframeId)}
|
|
162
|
+
${iframeAdjustViewport(window.innerHeight)}
|
|
163
|
+
</script>
|
|
164
|
+
</head>
|
|
165
|
+
<body>
|
|
166
|
+
${content}
|
|
167
|
+
</body>
|
|
168
|
+
</html>
|
|
169
|
+
`;
|
|
170
|
+
}
|
|
171
|
+
const Log = {
|
|
172
|
+
i (...msg) {
|
|
173
|
+
console.log('[MujianSDK] ', ...msg);
|
|
174
|
+
},
|
|
175
|
+
e (...msg) {
|
|
176
|
+
console.error('[MujianSDK] ', ...msg);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
9
179
|
addDOMPurifyHooks();
|
|
10
180
|
function canUseNegativeLookbehind() {
|
|
11
181
|
try {
|
|
@@ -95,7 +265,7 @@ function addDOMPurifyHooks() {
|
|
|
95
265
|
* @returns {string} Encoded message text
|
|
96
266
|
* @copyright https://github.com/kwaroran/risuAI
|
|
97
267
|
*/ function encodeStyleTags(text) {
|
|
98
|
-
const styleRegex = /<style>(
|
|
268
|
+
const styleRegex = /<style>([\s\S]+?)<\/style>/gi;
|
|
99
269
|
return text.replaceAll(styleRegex, (_, match)=>`<custom-style>${encodeURIComponent(match)}</custom-style>`);
|
|
100
270
|
}
|
|
101
271
|
/**
|
|
@@ -159,7 +329,7 @@ function addDOMPurifyHooks() {
|
|
|
159
329
|
const markdownUnderscoreExt = ()=>{
|
|
160
330
|
try {
|
|
161
331
|
if (!canUseNegativeLookbehind()) {
|
|
162
|
-
|
|
332
|
+
Log.i('Showdown-underscore extension: Negative lookbehind not supported. Skipping.');
|
|
163
333
|
return [];
|
|
164
334
|
}
|
|
165
335
|
return [
|
|
@@ -234,31 +404,184 @@ function messageFormatting(mes, sanitizerOverrides = {}) {
|
|
|
234
404
|
});
|
|
235
405
|
return mes;
|
|
236
406
|
}
|
|
237
|
-
const MdRendererBase = ({ content })=>{
|
|
238
|
-
const
|
|
407
|
+
const MdRendererBase = ({ content, unsafe = false, extra = {} })=>{
|
|
408
|
+
const containerRef = useRef(null);
|
|
409
|
+
const [iframeIdList, setIframeIdList] = useState([]);
|
|
410
|
+
useEffect(()=>{
|
|
411
|
+
let mes = messageFormatting(content);
|
|
412
|
+
mes = mes.replace(/<pre><code(.*)>[\s\S]*?<\/code><\/pre>/g, (match)=>{
|
|
413
|
+
if (!match.includes('<body>') && !match.includes('</body>')) return match;
|
|
414
|
+
const code = match.replace(/<pre><code(.*?)>/g, '').replace(/<\/code><\/pre>/g, '');
|
|
415
|
+
const id = v4();
|
|
416
|
+
const containerId = `MJ-iframe-container-${id}`;
|
|
417
|
+
const iframeId = `MJ-iframe-${id}`;
|
|
418
|
+
const srcdoc = createSrcContent(unescapeHTML(code), iframeId, unsafe, extra);
|
|
419
|
+
setIframeIdList((prev)=>[
|
|
420
|
+
...prev,
|
|
421
|
+
id
|
|
422
|
+
]);
|
|
423
|
+
const escapedSrcdoc = escapeHtmlAttribute(srcdoc);
|
|
424
|
+
return `
|
|
425
|
+
<div class="MJ-iframe-container" id="${containerId}" style="display:flex;width:100%;height:100%;position:relative;">
|
|
426
|
+
<div class="spin-overlay" id="MJ-spin-${id}" style="width:100%;height:100%;position:absolute;top:0;left:0;z-index:1000;background-color:rgba(0,0,0,0.5);display:flex;justify-content:center;align-items:center;">
|
|
427
|
+
<div class="spin-inner wave-text" style="font-weight:500;font-size:16px;color:white;display:flex;justify-content:center;align-items:center;">
|
|
428
|
+
<span>加</span>
|
|
429
|
+
<span>载</span>
|
|
430
|
+
<span>中</span>
|
|
431
|
+
<span>.</span>
|
|
432
|
+
<span>.</span>
|
|
433
|
+
<span>.</span>
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
<iframe id="${iframeId}" class="w-full" sandbox="${unsafe ? "allow-scripts" : "allow-scripts allow-same-origin"}" loading="lazy"
|
|
437
|
+
referrerpolicy="no-referrer" allowTransparency="true"
|
|
438
|
+
style="color-scheme: none;background-color: transparent;width:100%;;border:none;" srcdoc="` + escapedSrcdoc + `"></iframe>
|
|
439
|
+
</div>`;
|
|
440
|
+
});
|
|
441
|
+
if (containerRef.current) containerRef.current.innerHTML = mes;
|
|
442
|
+
}, [
|
|
443
|
+
content
|
|
444
|
+
]);
|
|
445
|
+
useEffect(()=>{
|
|
446
|
+
window.addEventListener('message', function(event) {
|
|
447
|
+
if (event.data?.type === 'MJ_ADJUST_IFRAME_HEIGHT' && event.data?.iframe_id) {
|
|
448
|
+
const targetIframe = containerRef.current?.querySelector('#' + event.data.iframe_id);
|
|
449
|
+
if (!targetIframe) return;
|
|
450
|
+
targetIframe.style.height = `${event.data.height}px`;
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
window.addEventListener('resize', ()=>{
|
|
454
|
+
iframeIdList.forEach((id)=>{
|
|
455
|
+
const iframe = containerRef.current?.querySelector('#MJ-iframe-' + id);
|
|
456
|
+
if (!iframe) return;
|
|
457
|
+
iframe.contentWindow?.postMessage({
|
|
458
|
+
type: 'MJ_UPDATE_VIEWPORT_HEIGHT'
|
|
459
|
+
}, '*');
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
}, []);
|
|
463
|
+
useEffect(()=>{
|
|
464
|
+
iframeIdList.forEach((id)=>{
|
|
465
|
+
const iframe = containerRef.current?.querySelector('#MJ-iframe-' + id);
|
|
466
|
+
if (!iframe) return;
|
|
467
|
+
const removeSpin = ()=>{
|
|
468
|
+
const spinElement = iframe.parentElement?.querySelector('#MJ-spin-' + id);
|
|
469
|
+
if (spinElement) spinElement.remove();
|
|
470
|
+
};
|
|
471
|
+
const timeout = setTimeout(removeSpin, 5000);
|
|
472
|
+
iframe.addEventListener('load', ()=>{
|
|
473
|
+
clearTimeout(timeout);
|
|
474
|
+
removeSpin();
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
return ()=>{
|
|
478
|
+
iframeIdList.forEach((id)=>{
|
|
479
|
+
const iframe = containerRef.current?.querySelector('#MJ-iframe-' + id);
|
|
480
|
+
if (!iframe) return;
|
|
481
|
+
const spinElement = iframe.parentElement?.querySelector('#MJ-spin-' + iframe.id);
|
|
482
|
+
if (spinElement) spinElement.remove();
|
|
483
|
+
iframe.removeEventListener('load', ()=>{
|
|
484
|
+
Log.i('iframe loaded', iframe.id);
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
};
|
|
488
|
+
}, [
|
|
489
|
+
iframeIdList
|
|
490
|
+
]);
|
|
239
491
|
return /*#__PURE__*/ jsx("div", {
|
|
240
492
|
className: "mes_text",
|
|
241
|
-
|
|
242
|
-
__html: mes
|
|
243
|
-
}
|
|
493
|
+
ref: containerRef
|
|
244
494
|
});
|
|
245
495
|
};
|
|
246
496
|
const MdRenderer = /*#__PURE__*/ react.memo(MdRendererBase, (prev, next)=>prev.content === next.content);
|
|
247
497
|
MdRendererBase.displayName = 'MdRenderer';
|
|
248
498
|
MdRenderer.displayName = 'MdRenderer';
|
|
249
|
-
|
|
250
|
-
|
|
499
|
+
var events_EVENT = /*#__PURE__*/ function(EVENT) {
|
|
500
|
+
EVENT["MUJIAN_INIT"] = "mujian:init";
|
|
501
|
+
EVENT["MUJIAN_AI_CHAT_STOP"] = "mujian:ai:chat:stop";
|
|
502
|
+
EVENT["MUJIAN_AI_CHAT_COMPLETE"] = "mujian:ai:chat:complete";
|
|
503
|
+
EVENT["MUJIAN_AI_CHAT_APPLY_REGEX"] = "mujian:ai:chat:applyRegex";
|
|
504
|
+
EVENT["MUJIAN_AI_CHAT_RENDER_MESSAGE"] = "mujian:ai:chat:renderMessage";
|
|
505
|
+
EVENT["MUJIAN_AI_TEXT_GENERATE"] = "mujian:ai:text:generate";
|
|
506
|
+
EVENT["MUJIAN_AI_OPENAI_COMPLETIONS_CREATE"] = "mujian:ai:openai:completions:create";
|
|
507
|
+
EVENT["MUJIAN_AI_OPENAI_CHAT_COMPLETIONS_CREATE"] = "mujian:ai:openai:chat:completions:create";
|
|
508
|
+
EVENT["MUJIAN_AI_OPENAI_RESPONSES_CREATE"] = "mujian:ai:openai:responses:create";
|
|
509
|
+
EVENT["MUJIAN_AI_CHAT_MESSAGE_GET_ALL"] = "mujian:ai:chat:message:getAll";
|
|
510
|
+
EVENT["MUJIAN_AI_CHAT_MESSAGE_GET_PAGE"] = "mujian:ai:chat:message:getPage";
|
|
511
|
+
EVENT["MUJIAN_AI_CHAT_PROJECT_GET_INFO"] = "mujian:ai:chat:project:getInfo";
|
|
512
|
+
EVENT["MUJIAN_AI_SETTINGS_PERSONA_GET_ACTIVE"] = "mujian:ai:settings:persona:getActive";
|
|
513
|
+
EVENT["MUJIAN_AI_SETTINGS_PERSONA_SET_ACTIVE"] = "mujian:ai:settings:persona:setActive";
|
|
514
|
+
EVENT["MUJIAN_AI_SETTINGS_MODEL_GET_ALL"] = "mujian:ai:settings:model:getAll";
|
|
515
|
+
EVENT["MUJIAN_AI_SETTINGS_MODEL_SET_ACTIVE"] = "mujian:ai:settings:model:setActive";
|
|
516
|
+
EVENT["MUJIAN_AI_SETTINGS_MODEL_GET_ACTIVE"] = "mujian:ai:settings:model:getActive";
|
|
517
|
+
EVENT["MUJIAN_AI_CHAT_MESSAGE_DELETE_ONE"] = "mujian:ai:chat:message:deleteOne";
|
|
518
|
+
EVENT["MUJIAN_AI_CHAT_MESSAGE_EDIT_ONE"] = "mujian:ai:chat:message:editOne";
|
|
519
|
+
EVENT["MUJIAN_AI_CHAT_MESSAGE_SWIPE"] = "mujian:ai:chat:message:swipe";
|
|
520
|
+
EVENT["MUJIAN_AI_CHAT_MESSAGE_GET_PROMPT"] = "mujian:ai:chat:message:getPrompt";
|
|
521
|
+
EVENT["MUJIAN_AI_OPENAI_IMAGES_GENERATE"] = "mujian:ai:openai:images:generate";
|
|
522
|
+
EVENT["MUJIAN_UTILS_CLIPBOARD_WRITE_TEXT"] = "mujian:utils:clipboard:writeText";
|
|
523
|
+
return EVENT;
|
|
524
|
+
}({});
|
|
525
|
+
function wrapOnData(onData) {
|
|
526
|
+
let fullContent = '';
|
|
527
|
+
let buffer = '';
|
|
528
|
+
let questionId;
|
|
529
|
+
let replyId;
|
|
530
|
+
return function(data) {
|
|
531
|
+
buffer += data;
|
|
532
|
+
const lines = buffer.split('\n');
|
|
533
|
+
buffer = lines.pop() || '';
|
|
534
|
+
for (const line of lines)if (line.startsWith('data: ')) try {
|
|
535
|
+
const parsedData = JSON.parse(line.slice(6));
|
|
536
|
+
if (parsedData.question_id) questionId = parsedData.question_id;
|
|
537
|
+
if (parsedData.reply_id) replyId = parsedData.reply_id;
|
|
538
|
+
if (parsedData.isFinished) return void onData({
|
|
539
|
+
isFinished: true,
|
|
540
|
+
deltaContent: '',
|
|
541
|
+
fullContent,
|
|
542
|
+
questionId,
|
|
543
|
+
replyId
|
|
544
|
+
});
|
|
545
|
+
const deltaContent = parsedData?.choices?.[0]?.delta?.content;
|
|
546
|
+
if (deltaContent?.length > 0) {
|
|
547
|
+
fullContent += deltaContent;
|
|
548
|
+
onData({
|
|
549
|
+
isFinished: false,
|
|
550
|
+
deltaContent: deltaContent,
|
|
551
|
+
fullContent,
|
|
552
|
+
questionId,
|
|
553
|
+
replyId
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
} catch (e) {
|
|
557
|
+
onData({
|
|
558
|
+
isFinished: true,
|
|
559
|
+
error: e,
|
|
560
|
+
deltaContent: '',
|
|
561
|
+
fullContent,
|
|
562
|
+
questionId,
|
|
563
|
+
replyId
|
|
564
|
+
});
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
const chat_complete = async function(message, onData, signal, option = {}) {
|
|
570
|
+
await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
|
|
251
571
|
content: message
|
|
252
572
|
}, {
|
|
253
|
-
onData,
|
|
573
|
+
onData: option.parseContent ? wrapOnData(onData) : onData,
|
|
254
574
|
signal
|
|
255
575
|
});
|
|
256
576
|
};
|
|
257
577
|
const applyRegex = async function(props) {
|
|
258
|
-
return await this.call(
|
|
578
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_APPLY_REGEX, props);
|
|
579
|
+
};
|
|
580
|
+
const renderMessage = async function(props) {
|
|
581
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_RENDER_MESSAGE, props);
|
|
259
582
|
};
|
|
260
583
|
const continueComplete = async function(onData, signal) {
|
|
261
|
-
return await this.call(
|
|
584
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
|
|
262
585
|
isContinue: true
|
|
263
586
|
}, {
|
|
264
587
|
onData,
|
|
@@ -266,7 +589,7 @@ const continueComplete = async function(onData, signal) {
|
|
|
266
589
|
});
|
|
267
590
|
};
|
|
268
591
|
const chat_regenerate = async function(onData, signal) {
|
|
269
|
-
return await this.call(
|
|
592
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
|
|
270
593
|
isRegenerate: true
|
|
271
594
|
}, {
|
|
272
595
|
onData,
|
|
@@ -274,61 +597,117 @@ const chat_regenerate = async function(onData, signal) {
|
|
|
274
597
|
});
|
|
275
598
|
};
|
|
276
599
|
const getAll = async function() {
|
|
277
|
-
return await this.call(
|
|
600
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_ALL);
|
|
278
601
|
};
|
|
279
602
|
const messageDeleteOne = async function(messageId) {
|
|
280
|
-
return await this.call(
|
|
603
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_DELETE_ONE, {
|
|
281
604
|
messageId
|
|
282
605
|
});
|
|
283
606
|
};
|
|
284
607
|
const messageEditOne = async function(messageId, content) {
|
|
285
|
-
return await this.call(
|
|
608
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_EDIT_ONE, {
|
|
286
609
|
messageId,
|
|
287
610
|
content
|
|
288
611
|
});
|
|
289
612
|
};
|
|
290
613
|
const messageSwipe = async function(messageId, swipeId) {
|
|
291
|
-
return await this.call(
|
|
614
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_SWIPE, {
|
|
292
615
|
messageId,
|
|
293
616
|
swipeId
|
|
294
617
|
});
|
|
295
618
|
};
|
|
296
619
|
const getPrompt = async function(messageId) {
|
|
297
|
-
return await this.call(
|
|
620
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_PROMPT, {
|
|
298
621
|
messageId
|
|
299
622
|
});
|
|
300
623
|
};
|
|
624
|
+
async function getPage(fromCursor, pageSize) {
|
|
625
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_PAGE, {
|
|
626
|
+
fromCursor,
|
|
627
|
+
pageSize
|
|
628
|
+
});
|
|
629
|
+
}
|
|
301
630
|
const getInfo = async function() {
|
|
302
|
-
return await this.call(
|
|
631
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_PROJECT_GET_INFO);
|
|
303
632
|
};
|
|
304
633
|
const getActive = async function() {
|
|
305
|
-
return await this.call(
|
|
634
|
+
return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_PERSONA_GET_ACTIVE);
|
|
306
635
|
};
|
|
307
636
|
const setActive = async function(personaId) {
|
|
308
|
-
return await this.call(
|
|
637
|
+
return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_PERSONA_SET_ACTIVE, {
|
|
309
638
|
personaId
|
|
310
639
|
});
|
|
311
640
|
};
|
|
312
641
|
const model_getActive = async function() {
|
|
313
|
-
return await this.call(
|
|
642
|
+
return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_GET_ACTIVE);
|
|
314
643
|
};
|
|
315
644
|
const model_setActive = async function(modelId) {
|
|
316
|
-
return await this.call(
|
|
645
|
+
return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_SET_ACTIVE, {
|
|
317
646
|
modelId
|
|
318
647
|
});
|
|
319
648
|
};
|
|
320
649
|
const model_getAll = async function() {
|
|
321
|
-
return await this.call(
|
|
650
|
+
return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_GET_ALL);
|
|
322
651
|
};
|
|
323
|
-
const generate = async function() {
|
|
324
|
-
return await this.call(
|
|
325
|
-
content
|
|
652
|
+
const generate = async function(content) {
|
|
653
|
+
return await this.call(MujianSdk.EVENT.MUJIAN_AI_TEXT_GENERATE, {
|
|
654
|
+
content
|
|
326
655
|
});
|
|
327
656
|
};
|
|
657
|
+
const chat = {
|
|
658
|
+
completions: {
|
|
659
|
+
create: async function(params, options, onData, signal) {
|
|
660
|
+
return await this.call(MujianSdk.EVENT.MUJIAN_AI_OPENAI_CHAT_COMPLETIONS_CREATE, {
|
|
661
|
+
params,
|
|
662
|
+
options
|
|
663
|
+
}, {
|
|
664
|
+
onData,
|
|
665
|
+
signal
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
const completions = {
|
|
671
|
+
create: async function(params, options, onData, signal) {
|
|
672
|
+
return await this.call(MujianSdk.EVENT.MUJIAN_AI_OPENAI_COMPLETIONS_CREATE, {
|
|
673
|
+
params,
|
|
674
|
+
options
|
|
675
|
+
}, {
|
|
676
|
+
onData,
|
|
677
|
+
signal
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
const responses = {
|
|
682
|
+
create: async function(params, options, onData, signal) {
|
|
683
|
+
return await this.call(MujianSdk.EVENT.MUJIAN_AI_OPENAI_RESPONSES_CREATE, {
|
|
684
|
+
params,
|
|
685
|
+
options
|
|
686
|
+
}, {
|
|
687
|
+
onData,
|
|
688
|
+
signal
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
const images_images = {
|
|
693
|
+
generate: async function(params, options, onData, signal) {
|
|
694
|
+
return await this.call(MujianSdk.EVENT.MUJIAN_AI_OPENAI_IMAGES_GENERATE, {
|
|
695
|
+
params,
|
|
696
|
+
options
|
|
697
|
+
}, {
|
|
698
|
+
onData,
|
|
699
|
+
signal
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
};
|
|
328
703
|
const saveGame = async function() {};
|
|
329
704
|
const loadGame = async function() {};
|
|
705
|
+
async function writeText(text) {
|
|
706
|
+
return await this.call(events_EVENT.MUJIAN_UTILS_CLIPBOARD_WRITE_TEXT, text);
|
|
707
|
+
}
|
|
330
708
|
class MujianSdk {
|
|
331
709
|
constructor(){}
|
|
710
|
+
static EVENT = events_EVENT;
|
|
332
711
|
static getInstance() {
|
|
333
712
|
if (!window.$mujian) window.$mujian = new MujianSdk();
|
|
334
713
|
return window.$mujian;
|
|
@@ -361,10 +740,25 @@ class MujianSdk {
|
|
|
361
740
|
const parent = await handshake;
|
|
362
741
|
this.ready = true;
|
|
363
742
|
this.parent = parent;
|
|
364
|
-
|
|
365
|
-
await this.call(
|
|
743
|
+
Log.i('mujian sdk client init');
|
|
744
|
+
await this.call(events_EVENT.MUJIAN_INIT);
|
|
745
|
+
const projectInfo = await this.ai.chat.project.getInfo();
|
|
746
|
+
if (projectInfo.config?.customCss) {
|
|
747
|
+
const style = document.createElement('style');
|
|
748
|
+
style.setAttribute('type', 'text/css');
|
|
749
|
+
style.setAttribute('id', 'mujian-custom-css');
|
|
750
|
+
style.textContent = projectInfo.config.customCss;
|
|
751
|
+
document.head.appendChild(style);
|
|
752
|
+
}
|
|
753
|
+
if (projectInfo.config?.customJs) {
|
|
754
|
+
const script = document.createElement("script");
|
|
755
|
+
script.setAttribute('type', "text/javascript");
|
|
756
|
+
script.setAttribute('id', 'mujian-custom-js');
|
|
757
|
+
script.textContent = projectInfo.config.customJs;
|
|
758
|
+
document.head.appendChild(script);
|
|
759
|
+
}
|
|
366
760
|
} catch (error) {
|
|
367
|
-
|
|
761
|
+
Log.e('init error', error);
|
|
368
762
|
}
|
|
369
763
|
}
|
|
370
764
|
emit(event, data) {
|
|
@@ -391,7 +785,7 @@ class MujianSdk {
|
|
|
391
785
|
data
|
|
392
786
|
});
|
|
393
787
|
controller?.signal?.addEventListener('abort', ()=>{
|
|
394
|
-
this.emit(
|
|
788
|
+
this.emit(events_EVENT.MUJIAN_AI_CHAT_STOP, {
|
|
395
789
|
id: callId
|
|
396
790
|
});
|
|
397
791
|
});
|
|
@@ -415,6 +809,7 @@ class MujianSdk {
|
|
|
415
809
|
},
|
|
416
810
|
complete: chat_complete.bind(this),
|
|
417
811
|
applyRegex: applyRegex.bind(this),
|
|
812
|
+
renderMessage: renderMessage.bind(this),
|
|
418
813
|
continue: continueComplete.bind(this),
|
|
419
814
|
regenerate: chat_regenerate.bind(this),
|
|
420
815
|
message: {
|
|
@@ -422,17 +817,34 @@ class MujianSdk {
|
|
|
422
817
|
deleteOne: messageDeleteOne.bind(this),
|
|
423
818
|
editOne: messageEditOne.bind(this),
|
|
424
819
|
swipe: messageSwipe.bind(this),
|
|
425
|
-
getPrompt: getPrompt.bind(this)
|
|
820
|
+
getPrompt: getPrompt.bind(this),
|
|
821
|
+
getPage: getPage.bind(this)
|
|
426
822
|
}
|
|
427
823
|
},
|
|
428
824
|
text: {
|
|
429
825
|
complete: generate.bind(this)
|
|
826
|
+
},
|
|
827
|
+
openai: {
|
|
828
|
+
completions: {
|
|
829
|
+
create: completions.create.bind(this)
|
|
830
|
+
},
|
|
831
|
+
chat: {
|
|
832
|
+
completions: {
|
|
833
|
+
create: chat.completions.create.bind(this)
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
responses: {
|
|
837
|
+
create: responses.create.bind(this)
|
|
838
|
+
},
|
|
839
|
+
images: {
|
|
840
|
+
generate: images_images.generate.bind(this)
|
|
841
|
+
}
|
|
430
842
|
}
|
|
431
843
|
};
|
|
432
844
|
ui = {};
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
845
|
+
utils = {
|
|
846
|
+
clipboard: {
|
|
847
|
+
writeText: writeText.bind(this)
|
|
436
848
|
}
|
|
437
849
|
};
|
|
438
850
|
hybrid = {};
|
|
@@ -450,6 +862,43 @@ class MujianSdk {
|
|
|
450
862
|
}
|
|
451
863
|
};
|
|
452
864
|
}
|
|
865
|
+
const styleSheet = `
|
|
866
|
+
@keyframes spin {
|
|
867
|
+
0% { transform: rotate(0deg); }
|
|
868
|
+
100% { transform: rotate(360deg); }
|
|
869
|
+
}
|
|
870
|
+
`;
|
|
871
|
+
const spinnerStyle = {
|
|
872
|
+
width: 48,
|
|
873
|
+
height: 48,
|
|
874
|
+
animation: 'spin 1s linear infinite'
|
|
875
|
+
};
|
|
876
|
+
const MujianSpinner = ({ className, style })=>/*#__PURE__*/ jsxs(Fragment, {
|
|
877
|
+
children: [
|
|
878
|
+
/*#__PURE__*/ jsx("style", {
|
|
879
|
+
children: styleSheet
|
|
880
|
+
}),
|
|
881
|
+
/*#__PURE__*/ jsx("svg", {
|
|
882
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
883
|
+
width: "24",
|
|
884
|
+
height: "24",
|
|
885
|
+
viewBox: "0 0 24 24",
|
|
886
|
+
fill: "none",
|
|
887
|
+
stroke: "currentColor",
|
|
888
|
+
strokeWidth: "2",
|
|
889
|
+
strokeLinecap: "round",
|
|
890
|
+
strokeLinejoin: "round",
|
|
891
|
+
className: className,
|
|
892
|
+
style: {
|
|
893
|
+
...spinnerStyle,
|
|
894
|
+
...style
|
|
895
|
+
},
|
|
896
|
+
children: /*#__PURE__*/ jsx("path", {
|
|
897
|
+
d: "M21 12a9 9 0 1 1-6.219-8.56"
|
|
898
|
+
})
|
|
899
|
+
})
|
|
900
|
+
]
|
|
901
|
+
});
|
|
453
902
|
const MujianContext = /*#__PURE__*/ createContext(null);
|
|
454
903
|
const MujianProvider = ({ children, loadingComponent })=>{
|
|
455
904
|
const [mujian, setMujian] = useState(null);
|
|
@@ -462,7 +911,20 @@ const MujianProvider = ({ children, loadingComponent })=>{
|
|
|
462
911
|
}, []);
|
|
463
912
|
return /*#__PURE__*/ jsx(MujianContext.Provider, {
|
|
464
913
|
value: mujian,
|
|
465
|
-
children: mujian ? children : loadingComponent ??
|
|
914
|
+
children: mujian ? children : loadingComponent ?? /*#__PURE__*/ jsx("div", {
|
|
915
|
+
style: {
|
|
916
|
+
height: '100%',
|
|
917
|
+
width: '100%',
|
|
918
|
+
display: 'flex',
|
|
919
|
+
justifyContent: 'center',
|
|
920
|
+
alignItems: 'center'
|
|
921
|
+
},
|
|
922
|
+
children: /*#__PURE__*/ jsx(MujianSpinner, {
|
|
923
|
+
style: {
|
|
924
|
+
color: '#EC4342'
|
|
925
|
+
}
|
|
926
|
+
})
|
|
927
|
+
})
|
|
466
928
|
});
|
|
467
929
|
};
|
|
468
930
|
const useMujian = ()=>{
|
|
@@ -480,7 +942,7 @@ const MessageItem = (props)=>{
|
|
|
480
942
|
const { data: renderedMessage } = useRequest(async ()=>{
|
|
481
943
|
const { isStreaming } = message;
|
|
482
944
|
if (isStreaming) return message;
|
|
483
|
-
return await mujian.ai.chat.
|
|
945
|
+
return await mujian.ai.chat.renderMessage({
|
|
484
946
|
message,
|
|
485
947
|
depth,
|
|
486
948
|
index
|
|
@@ -530,13 +992,6 @@ class UnexpectedSseEndingError extends Error {
|
|
|
530
992
|
this.cause = cause;
|
|
531
993
|
}
|
|
532
994
|
}
|
|
533
|
-
class InsufficientBalanceError extends Error {
|
|
534
|
-
constructor(message, cause){
|
|
535
|
-
super(message);
|
|
536
|
-
this.name = this.constructor.name;
|
|
537
|
-
this.cause = cause;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
995
|
async function* callSdk(mujian, content, type = 'generate', signal) {
|
|
541
996
|
const queue = [];
|
|
542
997
|
let resolveWait = null;
|
|
@@ -545,6 +1000,7 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
|
|
|
545
1000
|
try {
|
|
546
1001
|
const onData = (data)=>{
|
|
547
1002
|
if (error) return;
|
|
1003
|
+
if ('string' != typeof data) return;
|
|
548
1004
|
queue.push(data);
|
|
549
1005
|
if (resolveWait) {
|
|
550
1006
|
resolveWait();
|
|
@@ -580,6 +1036,8 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
|
|
|
580
1036
|
});
|
|
581
1037
|
}
|
|
582
1038
|
const NOT_SAVED_MSG_ID_PREFIX = 'not_saved';
|
|
1039
|
+
const FALLBACK_MESSAGE = `<!-- 此条HTML消息为系统提示,请勿复读 -->
|
|
1040
|
+
哎呀,AI线路好像抽风了,重说一下试试吧~`;
|
|
583
1041
|
const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
584
1042
|
const { body } = common;
|
|
585
1043
|
const [isStreaming, setIsStreaming] = useState(false);
|
|
@@ -594,16 +1052,22 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
594
1052
|
swipes: [],
|
|
595
1053
|
activeSwipeId: 0,
|
|
596
1054
|
isStreaming: false,
|
|
597
|
-
sendAt: new Date()
|
|
1055
|
+
sendAt: new Date(),
|
|
1056
|
+
swipeInfo: []
|
|
598
1057
|
};
|
|
599
1058
|
const aiMessage = {
|
|
600
1059
|
id: `${NOT_SAVED_MSG_ID_PREFIX}_assistant_${Date.now()}`,
|
|
601
1060
|
content: '',
|
|
602
1061
|
role: 'assistant',
|
|
603
|
-
swipes: [
|
|
1062
|
+
swipes: [
|
|
1063
|
+
''
|
|
1064
|
+
],
|
|
604
1065
|
activeSwipeId: 0,
|
|
605
1066
|
isStreaming: true,
|
|
606
|
-
sendAt: new Date()
|
|
1067
|
+
sendAt: new Date(),
|
|
1068
|
+
swipeInfo: [
|
|
1069
|
+
{}
|
|
1070
|
+
]
|
|
607
1071
|
};
|
|
608
1072
|
if (regenerate) setMessages((prev)=>{
|
|
609
1073
|
const newMessages = [
|
|
@@ -642,7 +1106,56 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
642
1106
|
const data = line.slice(6);
|
|
643
1107
|
try {
|
|
644
1108
|
const parsedData = JSON.parse(data);
|
|
645
|
-
if (
|
|
1109
|
+
if (regenerate || 'meta' !== parsedData.type) {
|
|
1110
|
+
if ('stream' === parsedData.type) {
|
|
1111
|
+
const partialContent = parsedData.choices[0].delta.content;
|
|
1112
|
+
parsedData.choices[0].delta.reasoning;
|
|
1113
|
+
content += partialContent;
|
|
1114
|
+
if (content || '' === content) {
|
|
1115
|
+
setMessages((prev)=>{
|
|
1116
|
+
const newMessages = [
|
|
1117
|
+
...prev
|
|
1118
|
+
];
|
|
1119
|
+
const lastMessage = newMessages[newMessages.length - 1];
|
|
1120
|
+
lastMessage.swipes[lastMessage.activeSwipeId] = content;
|
|
1121
|
+
newMessages[newMessages.length - 1] = {
|
|
1122
|
+
...lastMessage,
|
|
1123
|
+
content: content,
|
|
1124
|
+
isStreaming: true
|
|
1125
|
+
};
|
|
1126
|
+
return newMessages;
|
|
1127
|
+
});
|
|
1128
|
+
await new Promise((resolve)=>setTimeout(resolve, 10));
|
|
1129
|
+
} else {
|
|
1130
|
+
console.error('data', data);
|
|
1131
|
+
setError(new InvalidDeltaContentError(data));
|
|
1132
|
+
}
|
|
1133
|
+
} else if ('mjv' === parsedData.type) {
|
|
1134
|
+
const { value, delta, schema } = parsedData;
|
|
1135
|
+
setMessages((prev)=>{
|
|
1136
|
+
const newMessages = [
|
|
1137
|
+
...prev
|
|
1138
|
+
];
|
|
1139
|
+
const lastMessage = newMessages[newMessages.length - 1];
|
|
1140
|
+
const mjv = {
|
|
1141
|
+
value,
|
|
1142
|
+
delta,
|
|
1143
|
+
schema
|
|
1144
|
+
};
|
|
1145
|
+
const newSwipeInfo = lastMessage.swipeInfo;
|
|
1146
|
+
if (newSwipeInfo[lastMessage.activeSwipeId]) newSwipeInfo[lastMessage.activeSwipeId].mjv = mjv;
|
|
1147
|
+
else newSwipeInfo[lastMessage.activeSwipeId] = {
|
|
1148
|
+
mjv
|
|
1149
|
+
};
|
|
1150
|
+
newMessages[newMessages.length - 1] = {
|
|
1151
|
+
...lastMessage,
|
|
1152
|
+
swipeInfo: newSwipeInfo,
|
|
1153
|
+
mjv
|
|
1154
|
+
};
|
|
1155
|
+
return newMessages;
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
} else {
|
|
646
1159
|
const { question_id, reply_id } = parsedData;
|
|
647
1160
|
setMessages((prev)=>{
|
|
648
1161
|
const newMessages = [
|
|
@@ -658,29 +1171,6 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
658
1171
|
});
|
|
659
1172
|
continue;
|
|
660
1173
|
}
|
|
661
|
-
if (!parsedData.choices?.[0]) continue;
|
|
662
|
-
const partialContent = parsedData.choices[0].delta.content;
|
|
663
|
-
parsedData.choices[0].delta.reasoning;
|
|
664
|
-
content += partialContent;
|
|
665
|
-
if (content || '' === content) {
|
|
666
|
-
setMessages((prev)=>{
|
|
667
|
-
const newMessages = [
|
|
668
|
-
...prev
|
|
669
|
-
];
|
|
670
|
-
const lastMessage = newMessages[newMessages.length - 1];
|
|
671
|
-
lastMessage.swipes[lastMessage.activeSwipeId] = content;
|
|
672
|
-
newMessages[newMessages.length - 1] = {
|
|
673
|
-
...lastMessage,
|
|
674
|
-
content: content,
|
|
675
|
-
isStreaming: true
|
|
676
|
-
};
|
|
677
|
-
return newMessages;
|
|
678
|
-
});
|
|
679
|
-
await new Promise((resolve)=>setTimeout(resolve, 10));
|
|
680
|
-
} else {
|
|
681
|
-
console.error('data', data);
|
|
682
|
-
setError(new InvalidDeltaContentError(data));
|
|
683
|
-
}
|
|
684
1174
|
} catch {
|
|
685
1175
|
if ('[DONE]' !== data) {
|
|
686
1176
|
console.error('Received plain SSE message:', data);
|
|
@@ -689,22 +1179,30 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
689
1179
|
}
|
|
690
1180
|
}
|
|
691
1181
|
}
|
|
1182
|
+
} catch (error) {
|
|
1183
|
+
Log.e('Stream error', error);
|
|
1184
|
+
'object' == typeof error && error && 'message' in error && 'string' == typeof error.message ? setError(new SendMessageError(error.message)) : setError(error);
|
|
692
1185
|
} finally{
|
|
693
|
-
|
|
1186
|
+
Log.i('stream end finally');
|
|
694
1187
|
setMessages((prev)=>{
|
|
695
1188
|
const newMessages = [
|
|
696
1189
|
...prev
|
|
697
1190
|
];
|
|
698
|
-
|
|
1191
|
+
const lastMessage = {
|
|
699
1192
|
...newMessages[newMessages.length - 1],
|
|
700
1193
|
isStreaming: false
|
|
701
1194
|
};
|
|
1195
|
+
if (!lastMessage.swipes[lastMessage.activeSwipeId]) {
|
|
1196
|
+
lastMessage.swipes[lastMessage.activeSwipeId] = FALLBACK_MESSAGE;
|
|
1197
|
+
lastMessage.content = FALLBACK_MESSAGE;
|
|
1198
|
+
}
|
|
1199
|
+
newMessages[newMessages.length - 1] = lastMessage;
|
|
702
1200
|
return newMessages;
|
|
703
1201
|
});
|
|
704
1202
|
}
|
|
705
1203
|
} catch (error) {
|
|
706
1204
|
console.error('Error:', error);
|
|
707
|
-
|
|
1205
|
+
setError(new SendMessageError('Send message failed', error));
|
|
708
1206
|
setMessages((prev)=>prev.slice(0, -1));
|
|
709
1207
|
} finally{
|
|
710
1208
|
setIsStreaming(false);
|
|
@@ -718,9 +1216,8 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
718
1216
|
isStreaming
|
|
719
1217
|
};
|
|
720
1218
|
};
|
|
721
|
-
const useChat = ({ body, onError, onFinish })=>{
|
|
1219
|
+
const useChat = ({ body, onError, onFinish, pageSize })=>{
|
|
722
1220
|
const [error, _setError] = useState();
|
|
723
|
-
const [messages, setMessages] = useState([]);
|
|
724
1221
|
const [input, setInput] = useState('');
|
|
725
1222
|
const [abortController, setAbortController] = useState();
|
|
726
1223
|
const mujian = useMujian();
|
|
@@ -728,6 +1225,51 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
728
1225
|
_setError(error);
|
|
729
1226
|
if (error) onError?.(error);
|
|
730
1227
|
};
|
|
1228
|
+
const isLoadingMore = useRef(false);
|
|
1229
|
+
const { data, loadMoreAsync, loadingMore, loading, mutate, error: messageLoadError } = useInfiniteScroll(async (current)=>{
|
|
1230
|
+
isLoadingMore.current = true;
|
|
1231
|
+
if (current?.noMore) return {
|
|
1232
|
+
noMore: true,
|
|
1233
|
+
list: []
|
|
1234
|
+
};
|
|
1235
|
+
try {
|
|
1236
|
+
const resp = await mujian.ai.chat.message.getPage(current?.cursor, pageSize);
|
|
1237
|
+
return {
|
|
1238
|
+
cursor: resp.nextCursor,
|
|
1239
|
+
list: resp.messages.map((m)=>({
|
|
1240
|
+
...m,
|
|
1241
|
+
mjv: m.swipeInfo[m.activeSwipeId]?.mjv
|
|
1242
|
+
})),
|
|
1243
|
+
noMore: !resp.nextCursor
|
|
1244
|
+
};
|
|
1245
|
+
} finally{
|
|
1246
|
+
isLoadingMore.current = false;
|
|
1247
|
+
}
|
|
1248
|
+
}, {
|
|
1249
|
+
direction: 'top'
|
|
1250
|
+
});
|
|
1251
|
+
const loadMoreMessage = async ()=>{
|
|
1252
|
+
if (isLoadingMore.current) return;
|
|
1253
|
+
try {
|
|
1254
|
+
isLoadingMore.current = true;
|
|
1255
|
+
await loadMoreAsync();
|
|
1256
|
+
} finally{
|
|
1257
|
+
isLoadingMore.current = false;
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
const messages = data?.list ?? [];
|
|
1261
|
+
const latestData = useLatest(data);
|
|
1262
|
+
const latestListRef = useRef(messages);
|
|
1263
|
+
latestListRef.current = messages;
|
|
1264
|
+
const setMessages = (messages)=>{
|
|
1265
|
+
let newList;
|
|
1266
|
+
newList = 'function' == typeof messages ? messages(latestListRef.current) : messages;
|
|
1267
|
+
latestListRef.current = newList;
|
|
1268
|
+
mutate({
|
|
1269
|
+
...latestData.current,
|
|
1270
|
+
list: newList
|
|
1271
|
+
});
|
|
1272
|
+
};
|
|
731
1273
|
const { chatStreaming, isStreaming } = useChatStreaming({
|
|
732
1274
|
common: {
|
|
733
1275
|
body
|
|
@@ -736,10 +1278,6 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
736
1278
|
setMessages,
|
|
737
1279
|
mujian
|
|
738
1280
|
});
|
|
739
|
-
useMount(async ()=>{
|
|
740
|
-
const resp = await mujian.ai.chat.message.getAll();
|
|
741
|
-
setMessages(resp);
|
|
742
|
-
});
|
|
743
1281
|
useUpdateEffect(()=>{
|
|
744
1282
|
if (!isStreaming) {
|
|
745
1283
|
const lastMessage = messages[messages.length - 1];
|
|
@@ -748,48 +1286,79 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
748
1286
|
}, [
|
|
749
1287
|
isStreaming
|
|
750
1288
|
]);
|
|
751
|
-
const append = async (query)=>{
|
|
1289
|
+
const append = useCallback(async (query)=>{
|
|
752
1290
|
if (isStreaming) throw new Error('useChat is streaming already.');
|
|
753
1291
|
const controller = new AbortController();
|
|
754
1292
|
setAbortController(controller);
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
1293
|
+
try {
|
|
1294
|
+
await chatStreaming({
|
|
1295
|
+
query,
|
|
1296
|
+
signal: controller.signal
|
|
1297
|
+
});
|
|
1298
|
+
} catch (e) {
|
|
1299
|
+
setError(e);
|
|
1300
|
+
throw e;
|
|
1301
|
+
} finally{
|
|
1302
|
+
setAbortController(void 0);
|
|
1303
|
+
}
|
|
1304
|
+
}, [
|
|
1305
|
+
isStreaming,
|
|
1306
|
+
chatStreaming
|
|
1307
|
+
]);
|
|
1308
|
+
const regenerate = useCallback(async ()=>{
|
|
762
1309
|
if (isStreaming) throw new Error('useChat is streaming already.');
|
|
763
1310
|
const controller = new AbortController();
|
|
764
1311
|
setAbortController(controller);
|
|
765
1312
|
const lastMessage = messages.findLast((message)=>'user' === message.role);
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1313
|
+
try {
|
|
1314
|
+
await chatStreaming({
|
|
1315
|
+
query: lastMessage?.content || '',
|
|
1316
|
+
regenerate: true,
|
|
1317
|
+
signal: controller.signal
|
|
1318
|
+
});
|
|
1319
|
+
} catch (e) {
|
|
1320
|
+
setError(e);
|
|
1321
|
+
throw e;
|
|
1322
|
+
} finally{
|
|
1323
|
+
setAbortController(void 0);
|
|
1324
|
+
}
|
|
1325
|
+
}, [
|
|
1326
|
+
isStreaming,
|
|
1327
|
+
messages,
|
|
1328
|
+
chatStreaming
|
|
1329
|
+
]);
|
|
1330
|
+
const continueGenerate = useCallback(async ()=>{
|
|
774
1331
|
if (isStreaming) throw new Error('useChat is streaming already.');
|
|
775
1332
|
const controller = new AbortController();
|
|
776
1333
|
setAbortController(controller);
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1334
|
+
try {
|
|
1335
|
+
await chatStreaming({
|
|
1336
|
+
query: '',
|
|
1337
|
+
is_continue: true,
|
|
1338
|
+
signal: controller.signal
|
|
1339
|
+
});
|
|
1340
|
+
} catch (e) {
|
|
1341
|
+
setError(e);
|
|
1342
|
+
throw e;
|
|
1343
|
+
} finally{
|
|
1344
|
+
setAbortController(void 0);
|
|
1345
|
+
}
|
|
1346
|
+
}, [
|
|
1347
|
+
isStreaming,
|
|
1348
|
+
chatStreaming
|
|
1349
|
+
]);
|
|
784
1350
|
const stop = ()=>{
|
|
785
1351
|
abortController?.abort();
|
|
786
1352
|
};
|
|
787
1353
|
const setSwipe = async (messageId, swipeId)=>{
|
|
788
|
-
|
|
789
|
-
setMessages((prev)=>prev.map((msg)=>msg.id === messageId ? {
|
|
1354
|
+
if (swipeId < 0) return;
|
|
1355
|
+
setMessages((prev)=>prev.map((msg)=>msg.id === messageId && swipeId < msg.swipes.length ? {
|
|
790
1356
|
...msg,
|
|
791
|
-
activeSwipeId: swipeId
|
|
1357
|
+
activeSwipeId: swipeId,
|
|
1358
|
+
content: msg.swipes[swipeId],
|
|
1359
|
+
mjv: msg.swipeInfo[swipeId].mjv
|
|
792
1360
|
} : msg));
|
|
1361
|
+
await mujian.ai.chat.message.swipe(messageId, swipeId);
|
|
793
1362
|
};
|
|
794
1363
|
const editMessage = async (messageId, content)=>{
|
|
795
1364
|
await mujian.ai.chat.message.editOne(messageId, content);
|
|
@@ -809,7 +1378,11 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
809
1378
|
setMessages((prev)=>prev.map((msg)=>msg.id === messageId ? patchMessage(msg) : msg));
|
|
810
1379
|
};
|
|
811
1380
|
const deleteMessage = async (messageId)=>{
|
|
812
|
-
if (!messageId.startsWith(NOT_SAVED_MSG_ID_PREFIX))
|
|
1381
|
+
if (!messageId.startsWith(NOT_SAVED_MSG_ID_PREFIX)) try {
|
|
1382
|
+
await mujian.ai.chat.message.deleteOne(messageId);
|
|
1383
|
+
} catch (e) {
|
|
1384
|
+
if (e?.code !== 2011030004) throw e;
|
|
1385
|
+
}
|
|
813
1386
|
setMessages((prev)=>prev.filter((msg)=>msg.id !== messageId));
|
|
814
1387
|
};
|
|
815
1388
|
return {
|
|
@@ -832,7 +1405,9 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
832
1405
|
},
|
|
833
1406
|
setSwipe,
|
|
834
1407
|
editMessage,
|
|
835
|
-
deleteMessage
|
|
1408
|
+
deleteMessage,
|
|
1409
|
+
loadMoreMessage,
|
|
1410
|
+
messagesStatus: messageLoadError ? 'error' : loadingMore || loading ? 'loading' : data?.noMore ? 'all-loaded' : 'has-more'
|
|
836
1411
|
};
|
|
837
1412
|
};
|
|
838
|
-
export { MdRenderer, MujianProvider, Thread, useChat, useMujian };
|
|
1413
|
+
export { MdRenderer, MujianProvider, MujianSpinner, Thread, useChat, useMujian };
|