@mujian/js-sdk 0.0.6-beta.9 → 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 +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 +3 -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 +686 -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 +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,31 +403,184 @@ function messageFormatting(mes, sanitizerOverrides = {}) {
|
|
|
234
403
|
});
|
|
235
404
|
return mes;
|
|
236
405
|
}
|
|
237
|
-
const MdRendererBase = ({ content })=>{
|
|
238
|
-
const
|
|
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
|
+
]);
|
|
239
490
|
return /*#__PURE__*/ jsx("div", {
|
|
240
491
|
className: "mes_text",
|
|
241
|
-
|
|
242
|
-
__html: mes
|
|
243
|
-
}
|
|
492
|
+
ref: containerRef
|
|
244
493
|
});
|
|
245
494
|
};
|
|
246
495
|
const MdRenderer = /*#__PURE__*/ react.memo(MdRendererBase, (prev, next)=>prev.content === next.content);
|
|
247
496
|
MdRendererBase.displayName = 'MdRenderer';
|
|
248
497
|
MdRenderer.displayName = 'MdRenderer';
|
|
249
|
-
|
|
250
|
-
|
|
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, {
|
|
251
570
|
content: message
|
|
252
571
|
}, {
|
|
253
|
-
onData,
|
|
572
|
+
onData: option.parseContent ? wrapOnData(onData) : onData,
|
|
254
573
|
signal
|
|
255
574
|
});
|
|
256
575
|
};
|
|
257
576
|
const applyRegex = async function(props) {
|
|
258
|
-
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);
|
|
259
581
|
};
|
|
260
582
|
const continueComplete = async function(onData, signal) {
|
|
261
|
-
return await this.call(
|
|
583
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
|
|
262
584
|
isContinue: true
|
|
263
585
|
}, {
|
|
264
586
|
onData,
|
|
@@ -266,7 +588,7 @@ const continueComplete = async function(onData, signal) {
|
|
|
266
588
|
});
|
|
267
589
|
};
|
|
268
590
|
const chat_regenerate = async function(onData, signal) {
|
|
269
|
-
return await this.call(
|
|
591
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
|
|
270
592
|
isRegenerate: true
|
|
271
593
|
}, {
|
|
272
594
|
onData,
|
|
@@ -274,61 +596,117 @@ const chat_regenerate = async function(onData, signal) {
|
|
|
274
596
|
});
|
|
275
597
|
};
|
|
276
598
|
const getAll = async function() {
|
|
277
|
-
return await this.call(
|
|
599
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_ALL);
|
|
278
600
|
};
|
|
279
601
|
const messageDeleteOne = async function(messageId) {
|
|
280
|
-
return await this.call(
|
|
602
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_DELETE_ONE, {
|
|
281
603
|
messageId
|
|
282
604
|
});
|
|
283
605
|
};
|
|
284
606
|
const messageEditOne = async function(messageId, content) {
|
|
285
|
-
return await this.call(
|
|
607
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_EDIT_ONE, {
|
|
286
608
|
messageId,
|
|
287
609
|
content
|
|
288
610
|
});
|
|
289
611
|
};
|
|
290
612
|
const messageSwipe = async function(messageId, swipeId) {
|
|
291
|
-
return await this.call(
|
|
613
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_SWIPE, {
|
|
292
614
|
messageId,
|
|
293
615
|
swipeId
|
|
294
616
|
});
|
|
295
617
|
};
|
|
296
618
|
const getPrompt = async function(messageId) {
|
|
297
|
-
return await this.call(
|
|
619
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_PROMPT, {
|
|
298
620
|
messageId
|
|
299
621
|
});
|
|
300
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
|
+
}
|
|
301
629
|
const getInfo = async function() {
|
|
302
|
-
return await this.call(
|
|
630
|
+
return await this.call(events_EVENT.MUJIAN_AI_CHAT_PROJECT_GET_INFO);
|
|
303
631
|
};
|
|
304
632
|
const getActive = async function() {
|
|
305
|
-
return await this.call(
|
|
633
|
+
return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_PERSONA_GET_ACTIVE);
|
|
306
634
|
};
|
|
307
635
|
const setActive = async function(personaId) {
|
|
308
|
-
return await this.call(
|
|
636
|
+
return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_PERSONA_SET_ACTIVE, {
|
|
309
637
|
personaId
|
|
310
638
|
});
|
|
311
639
|
};
|
|
312
640
|
const model_getActive = async function() {
|
|
313
|
-
return await this.call(
|
|
641
|
+
return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_GET_ACTIVE);
|
|
314
642
|
};
|
|
315
643
|
const model_setActive = async function(modelId) {
|
|
316
|
-
return await this.call(
|
|
644
|
+
return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_SET_ACTIVE, {
|
|
317
645
|
modelId
|
|
318
646
|
});
|
|
319
647
|
};
|
|
320
648
|
const model_getAll = async function() {
|
|
321
|
-
return await this.call(
|
|
649
|
+
return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_GET_ALL);
|
|
322
650
|
};
|
|
323
|
-
const generate = async function() {
|
|
324
|
-
return await this.call(
|
|
325
|
-
content
|
|
651
|
+
const generate = async function(content) {
|
|
652
|
+
return await this.call(MujianSdk.EVENT.MUJIAN_AI_TEXT_GENERATE, {
|
|
653
|
+
content
|
|
326
654
|
});
|
|
327
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
|
+
};
|
|
328
702
|
const saveGame = async function() {};
|
|
329
703
|
const loadGame = async function() {};
|
|
704
|
+
async function writeText(text) {
|
|
705
|
+
return await this.call(events_EVENT.MUJIAN_UTILS_CLIPBOARD_WRITE_TEXT, text);
|
|
706
|
+
}
|
|
330
707
|
class MujianSdk {
|
|
331
708
|
constructor(){}
|
|
709
|
+
static EVENT = events_EVENT;
|
|
332
710
|
static getInstance() {
|
|
333
711
|
if (!window.$mujian) window.$mujian = new MujianSdk();
|
|
334
712
|
return window.$mujian;
|
|
@@ -361,10 +739,25 @@ class MujianSdk {
|
|
|
361
739
|
const parent = await handshake;
|
|
362
740
|
this.ready = true;
|
|
363
741
|
this.parent = parent;
|
|
364
|
-
|
|
365
|
-
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
|
+
}
|
|
366
759
|
} catch (error) {
|
|
367
|
-
|
|
760
|
+
Log.e('init error', error);
|
|
368
761
|
}
|
|
369
762
|
}
|
|
370
763
|
emit(event, data) {
|
|
@@ -391,7 +784,7 @@ class MujianSdk {
|
|
|
391
784
|
data
|
|
392
785
|
});
|
|
393
786
|
controller?.signal?.addEventListener('abort', ()=>{
|
|
394
|
-
this.emit(
|
|
787
|
+
this.emit(events_EVENT.MUJIAN_AI_CHAT_STOP, {
|
|
395
788
|
id: callId
|
|
396
789
|
});
|
|
397
790
|
});
|
|
@@ -415,6 +808,7 @@ class MujianSdk {
|
|
|
415
808
|
},
|
|
416
809
|
complete: chat_complete.bind(this),
|
|
417
810
|
applyRegex: applyRegex.bind(this),
|
|
811
|
+
renderMessage: renderMessage.bind(this),
|
|
418
812
|
continue: continueComplete.bind(this),
|
|
419
813
|
regenerate: chat_regenerate.bind(this),
|
|
420
814
|
message: {
|
|
@@ -422,17 +816,34 @@ class MujianSdk {
|
|
|
422
816
|
deleteOne: messageDeleteOne.bind(this),
|
|
423
817
|
editOne: messageEditOne.bind(this),
|
|
424
818
|
swipe: messageSwipe.bind(this),
|
|
425
|
-
getPrompt: getPrompt.bind(this)
|
|
819
|
+
getPrompt: getPrompt.bind(this),
|
|
820
|
+
getPage: getPage.bind(this)
|
|
426
821
|
}
|
|
427
822
|
},
|
|
428
823
|
text: {
|
|
429
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
|
+
}
|
|
430
841
|
}
|
|
431
842
|
};
|
|
432
843
|
ui = {};
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
844
|
+
utils = {
|
|
845
|
+
clipboard: {
|
|
846
|
+
writeText: writeText.bind(this)
|
|
436
847
|
}
|
|
437
848
|
};
|
|
438
849
|
hybrid = {};
|
|
@@ -450,6 +861,43 @@ class MujianSdk {
|
|
|
450
861
|
}
|
|
451
862
|
};
|
|
452
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
|
+
});
|
|
453
901
|
const MujianContext = /*#__PURE__*/ createContext(null);
|
|
454
902
|
const MujianProvider = ({ children, loadingComponent })=>{
|
|
455
903
|
const [mujian, setMujian] = useState(null);
|
|
@@ -462,7 +910,20 @@ const MujianProvider = ({ children, loadingComponent })=>{
|
|
|
462
910
|
}, []);
|
|
463
911
|
return /*#__PURE__*/ jsx(MujianContext.Provider, {
|
|
464
912
|
value: mujian,
|
|
465
|
-
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
|
+
})
|
|
466
927
|
});
|
|
467
928
|
};
|
|
468
929
|
const useMujian = ()=>{
|
|
@@ -480,7 +941,7 @@ const MessageItem = (props)=>{
|
|
|
480
941
|
const { data: renderedMessage } = useRequest(async ()=>{
|
|
481
942
|
const { isStreaming } = message;
|
|
482
943
|
if (isStreaming) return message;
|
|
483
|
-
return await mujian.ai.chat.
|
|
944
|
+
return await mujian.ai.chat.renderMessage({
|
|
484
945
|
message,
|
|
485
946
|
depth,
|
|
486
947
|
index
|
|
@@ -530,13 +991,6 @@ class UnexpectedSseEndingError extends Error {
|
|
|
530
991
|
this.cause = cause;
|
|
531
992
|
}
|
|
532
993
|
}
|
|
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
994
|
async function* callSdk(mujian, content, type = 'generate', signal) {
|
|
541
995
|
const queue = [];
|
|
542
996
|
let resolveWait = null;
|
|
@@ -545,6 +999,7 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
|
|
|
545
999
|
try {
|
|
546
1000
|
const onData = (data)=>{
|
|
547
1001
|
if (error) return;
|
|
1002
|
+
if ('string' != typeof data) return;
|
|
548
1003
|
queue.push(data);
|
|
549
1004
|
if (resolveWait) {
|
|
550
1005
|
resolveWait();
|
|
@@ -580,6 +1035,8 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
|
|
|
580
1035
|
});
|
|
581
1036
|
}
|
|
582
1037
|
const NOT_SAVED_MSG_ID_PREFIX = 'not_saved';
|
|
1038
|
+
const FALLBACK_MESSAGE = `<!-- 此条HTML消息为系统提示,请勿复读 -->
|
|
1039
|
+
哎呀,AI线路好像抽风了,重说一下试试吧~`;
|
|
583
1040
|
const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
584
1041
|
const { body } = common;
|
|
585
1042
|
const [isStreaming, setIsStreaming] = useState(false);
|
|
@@ -594,16 +1051,22 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
594
1051
|
swipes: [],
|
|
595
1052
|
activeSwipeId: 0,
|
|
596
1053
|
isStreaming: false,
|
|
597
|
-
sendAt: new Date()
|
|
1054
|
+
sendAt: new Date(),
|
|
1055
|
+
swipeInfo: []
|
|
598
1056
|
};
|
|
599
1057
|
const aiMessage = {
|
|
600
1058
|
id: `${NOT_SAVED_MSG_ID_PREFIX}_assistant_${Date.now()}`,
|
|
601
1059
|
content: '',
|
|
602
1060
|
role: 'assistant',
|
|
603
|
-
swipes: [
|
|
1061
|
+
swipes: [
|
|
1062
|
+
''
|
|
1063
|
+
],
|
|
604
1064
|
activeSwipeId: 0,
|
|
605
1065
|
isStreaming: true,
|
|
606
|
-
sendAt: new Date()
|
|
1066
|
+
sendAt: new Date(),
|
|
1067
|
+
swipeInfo: [
|
|
1068
|
+
{}
|
|
1069
|
+
]
|
|
607
1070
|
};
|
|
608
1071
|
if (regenerate) setMessages((prev)=>{
|
|
609
1072
|
const newMessages = [
|
|
@@ -642,7 +1105,56 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
642
1105
|
const data = line.slice(6);
|
|
643
1106
|
try {
|
|
644
1107
|
const parsedData = JSON.parse(data);
|
|
645
|
-
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 {
|
|
646
1158
|
const { question_id, reply_id } = parsedData;
|
|
647
1159
|
setMessages((prev)=>{
|
|
648
1160
|
const newMessages = [
|
|
@@ -658,29 +1170,6 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
658
1170
|
});
|
|
659
1171
|
continue;
|
|
660
1172
|
}
|
|
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
1173
|
} catch {
|
|
685
1174
|
if ('[DONE]' !== data) {
|
|
686
1175
|
console.error('Received plain SSE message:', data);
|
|
@@ -689,22 +1178,30 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
689
1178
|
}
|
|
690
1179
|
}
|
|
691
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);
|
|
692
1184
|
} finally{
|
|
693
|
-
|
|
1185
|
+
Log.i('stream end finally');
|
|
694
1186
|
setMessages((prev)=>{
|
|
695
1187
|
const newMessages = [
|
|
696
1188
|
...prev
|
|
697
1189
|
];
|
|
698
|
-
|
|
1190
|
+
const lastMessage = {
|
|
699
1191
|
...newMessages[newMessages.length - 1],
|
|
700
1192
|
isStreaming: false
|
|
701
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;
|
|
702
1199
|
return newMessages;
|
|
703
1200
|
});
|
|
704
1201
|
}
|
|
705
1202
|
} catch (error) {
|
|
706
1203
|
console.error('Error:', error);
|
|
707
|
-
|
|
1204
|
+
setError(new SendMessageError('Send message failed', error));
|
|
708
1205
|
setMessages((prev)=>prev.slice(0, -1));
|
|
709
1206
|
} finally{
|
|
710
1207
|
setIsStreaming(false);
|
|
@@ -718,9 +1215,8 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
718
1215
|
isStreaming
|
|
719
1216
|
};
|
|
720
1217
|
};
|
|
721
|
-
const useChat = ({ body, onError, onFinish })=>{
|
|
1218
|
+
const useChat = ({ body, onError, onFinish, pageSize })=>{
|
|
722
1219
|
const [error, _setError] = useState();
|
|
723
|
-
const [messages, setMessages] = useState([]);
|
|
724
1220
|
const [input, setInput] = useState('');
|
|
725
1221
|
const [abortController, setAbortController] = useState();
|
|
726
1222
|
const mujian = useMujian();
|
|
@@ -728,6 +1224,51 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
728
1224
|
_setError(error);
|
|
729
1225
|
if (error) onError?.(error);
|
|
730
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
|
+
};
|
|
731
1272
|
const { chatStreaming, isStreaming } = useChatStreaming({
|
|
732
1273
|
common: {
|
|
733
1274
|
body
|
|
@@ -736,10 +1277,6 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
736
1277
|
setMessages,
|
|
737
1278
|
mujian
|
|
738
1279
|
});
|
|
739
|
-
useMount(async ()=>{
|
|
740
|
-
const resp = await mujian.ai.chat.message.getAll();
|
|
741
|
-
setMessages(resp);
|
|
742
|
-
});
|
|
743
1280
|
useUpdateEffect(()=>{
|
|
744
1281
|
if (!isStreaming) {
|
|
745
1282
|
const lastMessage = messages[messages.length - 1];
|
|
@@ -748,48 +1285,79 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
748
1285
|
}, [
|
|
749
1286
|
isStreaming
|
|
750
1287
|
]);
|
|
751
|
-
const append = async (query)=>{
|
|
1288
|
+
const append = useCallback(async (query)=>{
|
|
752
1289
|
if (isStreaming) throw new Error('useChat is streaming already.');
|
|
753
1290
|
const controller = new AbortController();
|
|
754
1291
|
setAbortController(controller);
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
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 ()=>{
|
|
762
1308
|
if (isStreaming) throw new Error('useChat is streaming already.');
|
|
763
1309
|
const controller = new AbortController();
|
|
764
1310
|
setAbortController(controller);
|
|
765
1311
|
const lastMessage = messages.findLast((message)=>'user' === message.role);
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
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 ()=>{
|
|
774
1330
|
if (isStreaming) throw new Error('useChat is streaming already.');
|
|
775
1331
|
const controller = new AbortController();
|
|
776
1332
|
setAbortController(controller);
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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
|
+
]);
|
|
784
1349
|
const stop = ()=>{
|
|
785
1350
|
abortController?.abort();
|
|
786
1351
|
};
|
|
787
1352
|
const setSwipe = async (messageId, swipeId)=>{
|
|
788
|
-
|
|
789
|
-
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 ? {
|
|
790
1355
|
...msg,
|
|
791
|
-
activeSwipeId: swipeId
|
|
1356
|
+
activeSwipeId: swipeId,
|
|
1357
|
+
content: msg.swipes[swipeId],
|
|
1358
|
+
mjv: msg.swipeInfo[swipeId].mjv
|
|
792
1359
|
} : msg));
|
|
1360
|
+
await mujian.ai.chat.message.swipe(messageId, swipeId);
|
|
793
1361
|
};
|
|
794
1362
|
const editMessage = async (messageId, content)=>{
|
|
795
1363
|
await mujian.ai.chat.message.editOne(messageId, content);
|
|
@@ -809,7 +1377,11 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
809
1377
|
setMessages((prev)=>prev.map((msg)=>msg.id === messageId ? patchMessage(msg) : msg));
|
|
810
1378
|
};
|
|
811
1379
|
const deleteMessage = async (messageId)=>{
|
|
812
|
-
if (!messageId.startsWith(NOT_SAVED_MSG_ID_PREFIX))
|
|
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
|
+
}
|
|
813
1385
|
setMessages((prev)=>prev.filter((msg)=>msg.id !== messageId));
|
|
814
1386
|
};
|
|
815
1387
|
return {
|
|
@@ -832,7 +1404,9 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
832
1404
|
},
|
|
833
1405
|
setSwipe,
|
|
834
1406
|
editMessage,
|
|
835
|
-
deleteMessage
|
|
1407
|
+
deleteMessage,
|
|
1408
|
+
loadMoreMessage,
|
|
1409
|
+
messagesStatus: messageLoadError ? 'error' : loadingMore || loading ? 'loading' : data?.noMore ? 'all-loaded' : 'has-more'
|
|
836
1410
|
};
|
|
837
1411
|
};
|
|
838
|
-
export { MdRenderer, MujianProvider, Thread, useChat, useMujian };
|
|
1412
|
+
export { MdRenderer, MujianProvider, MujianSpinner, Thread, useChat, useMujian };
|