@agentscope-ai/chat 1.1.61-beta.1776007113818 → 1.1.61

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.
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useCallback, useRef, useState } from 'react';
1
+ import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import Bubble from '../../Bubble';
3
3
  import Input from '../Input';
4
4
  import { useProviderContext } from '@agentscope-ai/chat';
@@ -7,22 +7,80 @@ import { useTimeout } from 'ahooks';
7
7
  import { Disclaimer } from '@agentscope-ai/chat';
8
8
  import { useChatAnywhere } from '../hooks/ChatAnywhereProvider';
9
9
  import Style from './style';
10
+ import { TMessage } from '../hooks/types';
11
+ import { flushSync } from 'react-dom';
12
+
13
+ const PAGE_SIZE = 10;
14
+
15
+ /**
16
+ * 前端历史分页 Hook(仅在无外部 onLoadMore 时启用)
17
+ *
18
+ * 依赖 TMessage.history 标记区分消息类型:
19
+ * - history: true → session 加载时存在的历史消息,按页展示
20
+ * - history: false/undefined → 本次对话新产生的消息,始终全量展示
21
+ */
22
+ function useFrontendHistoryPagination(
23
+ messages: TMessage[],
24
+ currentSessionKey: string | undefined,
25
+ ) {
26
+ const safeMessages = useMemo(() => [...(messages || [])].reverse(), [messages]);
27
+ const [displayCount, setDisplayCount] = useState(PAGE_SIZE);
28
+
29
+ useEffect(() => {
30
+ setDisplayCount(PAGE_SIZE);
31
+ }, [currentSessionKey]);
32
+
33
+ const historyMessages = useMemo(() => safeMessages.filter(m => m.history), [safeMessages]);
34
+ const newMessages = useMemo(() => safeMessages.filter(m => !m.history), [safeMessages]);
35
+
36
+ const visibleHistory = historyMessages.slice(0, displayCount);
37
+ const noMore = displayCount >= historyMessages.length;
38
+
39
+ // 新消息在前(最新),历史分页消息在后(较旧)
40
+ const visibleMessages = useMemo(
41
+ () => [...newMessages, ...visibleHistory],
42
+ [newMessages, visibleHistory],
43
+ );
44
+
45
+ const loadMore = useCallback(
46
+ () =>
47
+ new Promise<void>((resolve) => {
48
+ setTimeout(() => {
49
+ flushSync(() => setDisplayCount((prev) => prev + PAGE_SIZE));
50
+ resolve();
51
+ }, 300);
52
+ }),
53
+ [],
54
+ );
55
+
56
+ return { visibleMessages, noMore, loadMore };
57
+ }
10
58
 
11
59
  export default forwardRef(function (_, ref) {
12
60
  const messages = useChatAnywhere(v => v.messages);
13
61
  const setMessages = useChatAnywhere(v => v.setMessages);
14
62
  const onLoadMore = useChatAnywhere(v => v.onLoadMore);
15
- const safeMessages = [...(messages || [])].reverse();
16
63
  const { getPrefixCls } = useProviderContext();
17
64
  const prefixCls = getPrefixCls('chat-anywhere');
18
65
  const uiConfig = useChatAnywhere(v => v.uiConfig);
19
66
  const currentSessionKey = useChatAnywhere(v => v.currentSessionKey);
20
67
  const [ready, setReady] = useState(false);
21
- const [noMore, setNoMore] = useState(false);
68
+ const [backendNoMore, setBackendNoMore] = useState(false);
22
69
  const loadingMoreRef = useRef(false);
23
70
 
24
- React.useEffect(() => {
25
- setNoMore(false);
71
+ const isBackendPagination = typeof onLoadMore === 'function';
72
+
73
+ const {
74
+ visibleMessages,
75
+ noMore: frontendNoMore,
76
+ loadMore: frontendLoadMore,
77
+ } = useFrontendHistoryPagination(
78
+ isBackendPagination ? [] : (messages ?? []),
79
+ currentSessionKey,
80
+ );
81
+
82
+ useEffect(() => {
83
+ setBackendNoMore(false);
26
84
  loadingMoreRef.current = false;
27
85
  }, [currentSessionKey]);
28
86
 
@@ -30,7 +88,7 @@ export default forwardRef(function (_, ref) {
30
88
  setReady(true);
31
89
  }, 300);
32
90
 
33
- const handleLoadMore = useCallback(async () => {
91
+ const handleBackendLoadMore = useCallback(async () => {
34
92
  if (!onLoadMore || loadingMoreRef.current) return;
35
93
  loadingMoreRef.current = true;
36
94
  try {
@@ -38,12 +96,18 @@ export default forwardRef(function (_, ref) {
38
96
  if (result?.messages?.length) {
39
97
  setMessages(prev => [...result.messages, ...prev]);
40
98
  }
41
- setNoMore(result?.noMore ?? false);
99
+ setBackendNoMore(result?.noMore ?? false);
42
100
  } finally {
43
101
  loadingMoreRef.current = false;
44
102
  }
45
103
  }, [onLoadMore, setMessages]);
46
104
 
105
+ const displayMessages = isBackendPagination
106
+ ? [...(messages || [])].reverse()
107
+ : visibleMessages;
108
+ const noMore = isBackendPagination ? backendNoMore : frontendNoMore;
109
+ const handleLoadMore = isBackendPagination ? handleBackendLoadMore : frontendLoadMore;
110
+
47
111
  const chatClassName = cls(
48
112
  `${prefixCls}-chat`,
49
113
  {
@@ -51,19 +115,20 @@ export default forwardRef(function (_, ref) {
51
115
  }
52
116
  );
53
117
 
54
- const emptyMessage = safeMessages.length === 0;
118
+ // 用实际 messages 数量判断空态,避免分页时 welcome 屏闪烁
119
+ const emptyMessage = (messages || []).length === 0;
55
120
 
56
121
  return <>
57
122
  <Style />
58
123
  <div className={chatClassName}>
59
124
  <Bubble.List
60
- onLoadMore={onLoadMore ? handleLoadMore : undefined}
61
- noMore={onLoadMore ? noMore : undefined}
125
+ onLoadMore={noMore ? undefined : handleLoadMore}
126
+ noMore={noMore}
62
127
  order="desc"
63
128
  style={{ height: 0, flex: emptyMessage ? 0 : 1 }}
64
129
  // @ts-ignore
65
130
  ref={ref.chatRef}
66
- items={safeMessages}
131
+ items={displayMessages}
67
132
  />
68
133
  {
69
134
  emptyMessage ? <div className={`${chatClassName}-welcome`}>{uiConfig?.welcome}</div> : null
@@ -80,4 +145,4 @@ export default forwardRef(function (_, ref) {
80
145
  }
81
146
  </div>
82
147
  </>
83
- })
148
+ })
@@ -45,7 +45,7 @@ export function InnerSession() {
45
45
 
46
46
  useEffect(() => {
47
47
  const messages = getMessagesBySession(currentSessionKey, currentRegenerateIndex);
48
- setMessages(messages);
48
+ setMessages((messages || []).map(m => ({ ...m, history: true })));
49
49
  }, [currentSessionKey, currentRegenerateIndex]);
50
50
 
51
51
  return <div className={`${prefixCls}-session`} >
@@ -69,6 +69,11 @@ export interface TMessage {
69
69
  * @descriptionEn Processing status of the message, affects display effects
70
70
  */
71
71
  msgStatus?: 'finished' | 'interrupted' | 'generating' | 'error';
72
+ /**
73
+ * @description 是否为历史消息(session 加载时存在),供前端分页使用
74
+ * @descriptionEn Whether the message is a historical message (existed at session load), used for frontend pagination
75
+ */
76
+ history?: boolean;
72
77
  }
73
78
 
74
79
  export interface IChatAnywhereConfigUIConfig {
@@ -15,7 +15,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
15
15
  function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
16
16
  function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
17
17
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
18
- import React, { forwardRef, useCallback, useRef, useState } from 'react';
18
+ import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
19
19
  import Bubble from "../../Bubble";
20
20
  import Input from "../Input";
21
21
  import { useProviderContext } from "../..";
@@ -24,9 +24,65 @@ import { useTimeout } from 'ahooks';
24
24
  import { Disclaimer } from "../..";
25
25
  import { useChatAnywhere } from "../hooks/ChatAnywhereProvider";
26
26
  import Style from "./style";
27
+ import { flushSync } from 'react-dom';
27
28
  import { jsx as _jsx } from "react/jsx-runtime";
28
29
  import { jsxs as _jsxs } from "react/jsx-runtime";
29
30
  import { Fragment as _Fragment } from "react/jsx-runtime";
31
+ var PAGE_SIZE = 10;
32
+
33
+ /**
34
+ * 前端历史分页 Hook(仅在无外部 onLoadMore 时启用)
35
+ *
36
+ * 依赖 TMessage.history 标记区分消息类型:
37
+ * - history: true → session 加载时存在的历史消息,按页展示
38
+ * - history: false/undefined → 本次对话新产生的消息,始终全量展示
39
+ */
40
+ function useFrontendHistoryPagination(messages, currentSessionKey) {
41
+ var safeMessages = useMemo(function () {
42
+ return _toConsumableArray(messages || []).reverse();
43
+ }, [messages]);
44
+ var _useState = useState(PAGE_SIZE),
45
+ _useState2 = _slicedToArray(_useState, 2),
46
+ displayCount = _useState2[0],
47
+ setDisplayCount = _useState2[1];
48
+ useEffect(function () {
49
+ setDisplayCount(PAGE_SIZE);
50
+ }, [currentSessionKey]);
51
+ var historyMessages = useMemo(function () {
52
+ return safeMessages.filter(function (m) {
53
+ return m.history;
54
+ });
55
+ }, [safeMessages]);
56
+ var newMessages = useMemo(function () {
57
+ return safeMessages.filter(function (m) {
58
+ return !m.history;
59
+ });
60
+ }, [safeMessages]);
61
+ var visibleHistory = historyMessages.slice(0, displayCount);
62
+ var noMore = displayCount >= historyMessages.length;
63
+
64
+ // 新消息在前(最新),历史分页消息在后(较旧)
65
+ var visibleMessages = useMemo(function () {
66
+ return [].concat(_toConsumableArray(newMessages), _toConsumableArray(visibleHistory));
67
+ }, [newMessages, visibleHistory]);
68
+ var loadMore = useCallback(function () {
69
+ return new Promise(function (resolve) {
70
+ setTimeout(function () {
71
+ flushSync(function () {
72
+ return setDisplayCount(function (prev) {
73
+ return prev + PAGE_SIZE;
74
+ });
75
+ });
76
+ resolve();
77
+ }, 300);
78
+ });
79
+ }, []);
80
+ return {
81
+ visibleMessages: visibleMessages,
82
+ noMore: noMore,
83
+ loadMore: loadMore
84
+ };
85
+ }
30
86
  export default /*#__PURE__*/forwardRef(function (_, ref) {
31
87
  var messages = useChatAnywhere(function (v) {
32
88
  return v.messages;
@@ -37,7 +93,6 @@ export default /*#__PURE__*/forwardRef(function (_, ref) {
37
93
  var onLoadMore = useChatAnywhere(function (v) {
38
94
  return v.onLoadMore;
39
95
  });
40
- var safeMessages = _toConsumableArray(messages || []).reverse();
41
96
  var _useProviderContext = useProviderContext(),
42
97
  getPrefixCls = _useProviderContext.getPrefixCls;
43
98
  var prefixCls = getPrefixCls('chat-anywhere');
@@ -47,23 +102,28 @@ export default /*#__PURE__*/forwardRef(function (_, ref) {
47
102
  var currentSessionKey = useChatAnywhere(function (v) {
48
103
  return v.currentSessionKey;
49
104
  });
50
- var _useState = useState(false),
51
- _useState2 = _slicedToArray(_useState, 2),
52
- ready = _useState2[0],
53
- setReady = _useState2[1];
54
105
  var _useState3 = useState(false),
55
106
  _useState4 = _slicedToArray(_useState3, 2),
56
- noMore = _useState4[0],
57
- setNoMore = _useState4[1];
107
+ ready = _useState4[0],
108
+ setReady = _useState4[1];
109
+ var _useState5 = useState(false),
110
+ _useState6 = _slicedToArray(_useState5, 2),
111
+ backendNoMore = _useState6[0],
112
+ setBackendNoMore = _useState6[1];
58
113
  var loadingMoreRef = useRef(false);
59
- React.useEffect(function () {
60
- setNoMore(false);
114
+ var isBackendPagination = typeof onLoadMore === 'function';
115
+ var _useFrontendHistoryPa = useFrontendHistoryPagination(isBackendPagination ? [] : messages !== null && messages !== void 0 ? messages : [], currentSessionKey),
116
+ visibleMessages = _useFrontendHistoryPa.visibleMessages,
117
+ frontendNoMore = _useFrontendHistoryPa.noMore,
118
+ frontendLoadMore = _useFrontendHistoryPa.loadMore;
119
+ useEffect(function () {
120
+ setBackendNoMore(false);
61
121
  loadingMoreRef.current = false;
62
122
  }, [currentSessionKey]);
63
123
  useTimeout(function () {
64
124
  setReady(true);
65
125
  }, 300);
66
- var handleLoadMore = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
126
+ var handleBackendLoadMore = useCallback( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
67
127
  var _result$messages, _result$noMore, result;
68
128
  return _regeneratorRuntime().wrap(function _callee$(_context) {
69
129
  while (1) switch (_context.prev = _context.next) {
@@ -85,7 +145,7 @@ export default /*#__PURE__*/forwardRef(function (_, ref) {
85
145
  return [].concat(_toConsumableArray(result.messages), _toConsumableArray(prev));
86
146
  });
87
147
  }
88
- setNoMore((_result$noMore = result === null || result === void 0 ? void 0 : result.noMore) !== null && _result$noMore !== void 0 ? _result$noMore : false);
148
+ setBackendNoMore((_result$noMore = result === null || result === void 0 ? void 0 : result.noMore) !== null && _result$noMore !== void 0 ? _result$noMore : false);
89
149
  case 9:
90
150
  _context.prev = 9;
91
151
  loadingMoreRef.current = false;
@@ -96,14 +156,19 @@ export default /*#__PURE__*/forwardRef(function (_, ref) {
96
156
  }
97
157
  }, _callee, null, [[3,, 9, 12]]);
98
158
  })), [onLoadMore, setMessages]);
159
+ var displayMessages = isBackendPagination ? _toConsumableArray(messages || []).reverse() : visibleMessages;
160
+ var noMore = isBackendPagination ? backendNoMore : frontendNoMore;
161
+ var handleLoadMore = isBackendPagination ? handleBackendLoadMore : frontendLoadMore;
99
162
  var chatClassName = cls("".concat(prefixCls, "-chat"), _defineProperty({}, "".concat(prefixCls, "-chat-hide"), !ready));
100
- var emptyMessage = safeMessages.length === 0;
163
+
164
+ // 用实际 messages 数量判断空态,避免分页时 welcome 屏闪烁
165
+ var emptyMessage = (messages || []).length === 0;
101
166
  return /*#__PURE__*/_jsxs(_Fragment, {
102
167
  children: [/*#__PURE__*/_jsx(Style, {}), /*#__PURE__*/_jsxs("div", {
103
168
  className: chatClassName,
104
169
  children: [/*#__PURE__*/_jsx(Bubble.List, {
105
- onLoadMore: onLoadMore ? handleLoadMore : undefined,
106
- noMore: onLoadMore ? noMore : undefined,
170
+ onLoadMore: noMore ? undefined : handleLoadMore,
171
+ noMore: noMore,
107
172
  order: "desc",
108
173
  style: {
109
174
  height: 0,
@@ -112,7 +177,7 @@ export default /*#__PURE__*/forwardRef(function (_, ref) {
112
177
  // @ts-ignore
113
178
  ,
114
179
  ref: ref.chatRef,
115
- items: safeMessages
180
+ items: displayMessages
116
181
  }), emptyMessage ? /*#__PURE__*/_jsx("div", {
117
182
  className: "".concat(chatClassName, "-welcome"),
118
183
  children: uiConfig === null || uiConfig === void 0 ? void 0 : uiConfig.welcome
@@ -1,3 +1,9 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
5
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
6
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
1
7
  import React, { useEffect } from 'react';
2
8
  import { Conversations } from "../..";
3
9
  import cls from 'classnames';
@@ -48,7 +54,11 @@ export function InnerSession() {
48
54
  var isMobile = isMobileHook();
49
55
  useEffect(function () {
50
56
  var messages = getMessagesBySession(currentSessionKey, currentRegenerateIndex);
51
- setMessages(messages);
57
+ setMessages((messages || []).map(function (m) {
58
+ return _objectSpread(_objectSpread({}, m), {}, {
59
+ history: true
60
+ });
61
+ }));
52
62
  }, [currentSessionKey, currentRegenerateIndex]);
53
63
  return /*#__PURE__*/_jsx("div", {
54
64
  className: "".concat(prefixCls, "-session"),
@@ -67,6 +67,11 @@ export interface TMessage {
67
67
  * @descriptionEn Processing status of the message, affects display effects
68
68
  */
69
69
  msgStatus?: 'finished' | 'interrupted' | 'generating' | 'error';
70
+ /**
71
+ * @description 是否为历史消息(session 加载时存在),供前端分页使用
72
+ * @descriptionEn Whether the message is a historical message (existed at session load), used for frontend pagination
73
+ */
74
+ history?: boolean;
70
75
  }
71
76
  export interface IChatAnywhereConfigUIConfig {
72
77
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentscope-ai/chat",
3
- "version": "1.1.61-beta.1776007113818",
3
+ "version": "1.1.61",
4
4
  "description": "a free and open-source chat framework for building excellent LLM-powered chat experiences",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": [