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